Skip to content

Astro + Vite 生产环境变量为何失效:PUBLIC_ 前缀是关键 ​

导语 ​

在 Astro + Vite 项目中,开发者常遇到一个典型问题:本地开发一切正常,生产构建却抛出 TypeError: Cannot read properties of undefined (reading "API_URL")。.env 文件存在、变量已定义,但生产环境中它就是 undefined。本文译自 dev.to 上的一篇排查记录,作者拆解了这一常见陷阱的成因与两种修复路径。

译注:本文原文发布于 dev.to,作者为 popdasedev,原文链接见文末。

问题现象 ​

作者在生产构建中遇到的报错如下:

TypeError: Cannot read properties of undefined (reading "API_URL")

同样的代码在开发环境运行良好,.env 文件就在那里,变量也确实定义了。然而到了生产环境,它变成了 undefined。这是 Vite 与 Astro 最常见的坑之一,而错误信息本身几乎不提供任何线索。

核心规则:只有 PUBLIC_ 前缀会暴露给客户端 ​

Vite 只会把以 PUBLIC_ 为前缀的环境变量暴露给客户端,其余全部保留在服务端。

env
PUBLIC_API_URL=https://api.example.com
API_SECRET=abc123

如果你在客户端组件中引用 import.meta.env.API_SECRET,结果就是 undefined。这不是变量缺失,而是 Vite 有意隐藏了它。这是特性而非缺陷——它能防止你把 API 密钥打包发给每一位访客。

典型症状 ​

  • .env 中定义了 API_KEY
  • 组件调用了 import.meta.env.API_KEY
  • 开发环境正常工作
  • 生产环境返回 undefined
  • 代码抛出 TypeError

Vite 在开发模式下较为宽松,而在生产构建阶段,任何没有 PUBLIC_ 前缀的变量都会被剥离。

修复方案 ​

方案一:重命名变量 ​

如果该值可以安全暴露:

env
PUBLIC_API_URL=https://api.example.com

然后用新名称引用:

js
const apiUrl = import.meta.env.PUBLIC_API_URL

方案二:保留在服务端 ​

如果该值是密钥,把它移入 Astro API 路由,例如 src/pages/api/something.json.ts:

js
export const prerender = false

export async function GET() {
  const response = await fetch("https://api.example.com/data", {
    headers: { Authorization: `Bearer ${import.meta.env.API_SECRET}` }
  })
  const data = await response.json()
  return new Response(JSON.stringify(data))
}

随后在客户端调用 /api/something.json,密钥始终留在服务端。

Cloudflare 的隐藏陷阱 ​

即使修复了前缀问题,第二个问题往往还会出现。.env 文件不应提交到 Git,而 Cloudflare 也不会读取它。你需要在 Cloudflare 控制台的 Settings → Environment variables 中,为 Production 和 Preview 分别设置相同的变量,然后触发一次新的部署。

通用原则 ​

变量类型可见范围是否可暴露
PUBLIC_ 前缀客户端与服务端安全
其他任意变量仅服务端不安全

只要一个值没有 PUBLIC_ 前缀,就应当把它当作密钥对待。

原文链接 ​

https://dev.to/popdasedev/why-my-env-variables-were-undefined-in-production-astro-vite-1ac1