一个预订链接在服务器上跑得好好的,到了浏览器里却显示成undefined。没有红色报错,构建日志还显示退出码 0。这种"静默失败"在 Next.js 应用里并不罕见,尤其是当项目由 AI 工具生成、或者变量是后来才补上的时候。
有人在一个小型合成应用上复现了这个现象,应用了最小的修复方案,并在无头 Chrome 里点击按钮做了验证。结论很直接:要么变量名少了NEXT_PUBLIC_前缀,要么变量是在应用构建完成之后才加进去的。
症状长什么样
没有红色错误提示。在无头 Chrome 里点击"Book a call"按钮后,页面上显示的是undefined/book。页面代码读的是process.env.BOOKING_URL + "/book",而.env.local里明明写着BOOKING_URL=https://booking.example.test。构建日志甚至打印出 Environments: .env.local,然后正常退出。
如果代码把这个值发给某个服务,通常会在浏览器的网络面板里看到失败的请求,或者 URL 里夹着一个 undefined。不过这一部分没有被测试。
原因:浏览器看不到服务器的设置
Next.js 的代码在两个地方运行:服务器和访客的浏览器。浏览器无法读取服务器上的设置。为了让浏览器能看到某个值,Next.js 会在执行next build时把这个值复制进下载的 JavaScript 里——但只针对以NEXT_PUBLIC_开头的变量,而且只在这个变量当时存在的情况下。
这就带来两种常见的 undefined:
- 没有前缀。BOOKING_URL 留在了服务器上。在浏览器里,process.env.BOOKING_URL 是空的。在构建产物里,代码在点击时仍然是一次查找:process.env.BOOKING_URL+"/book"。
- 加得太晚。NEXT_PUBLIC_BOOKING_URL 名字是对的,但在 next build 运行时它还没被设置(比如你在上次部署之后才把它加到托管平台的设置里)。这个值在构建时就被"冻结"了,浏览器仍然拿到 undefined,直到你重新构建。
第二种情况也被复现了:在没有变量的情况下构建,然后添加 .env.local 并重启 next start,结果依然是 undefined。
最小的修复方案
- 在所有设置它的地方(.env.local 和托管平台设置)把变量从 BOOKING_URL 改名为NEXT_PUBLIC_BOOKING_URL
- 把读取它的代码改成同一个名字,严格写成
process.env.NEXT_PUBLIC_BOOKING_URL - 重新构建并重新部署。本地操作是停掉服务器,运行 npx next build,再启动;开发模式下停掉并重启 npm run dev。在托管平台上,保存变量后触发一次新部署。
修改前,点击后显示 undefined/book;修改后,页面显示https://booking.example.test/book。
写法必须精确。按 Next.js 文档和相关讨论,像process.env[name]、先把 process.env 存进一个变量、或者可选链(process?.env?.NAME)这些形式都不会被替换。这些形式没有被测试。
验证过程
验证在 2026 年 10 月 24 日进行,使用一个小型合成应用(三份副本:修改前、修改后、加得太晚),配合默认设置的无头 Chrome。
- 两种情况下构建都通过:next build 在修改前后都退出 0。构建本身抓不到这个问题。
- 修改前,生产模式(next build + next start):点击后页面显示 undefined/book。
- 修改前,开发模式(next dev):结果相同,undefined/book。
- 修改后,生产模式和开发模式:页面显示 https://booking.example.test/book。
- 产物检查:构建后的"修改前"代码在点击时仍然读取 process.env.BOOKING_URL,而"修改后"的代码已经把链接写进去了。
"加得太晚"的情况:没有 .env.local 时构建再启动,显示 undefined/book;然后添加 .env.local 并重启服务器但不重新构建,仍然是 undefined/book;再重新构建并重启,才显示 https://booking.example.test/book。
每次运行还记录到一个无关的 404 资源错误,没有确认是哪个文件(可能是缺失的图标文件)。
安全提醒
任何以 NEXT_PUBLIC_ 命名的值都会进入任何人都能下载和阅读的代码里。只用于本来就该公开的值,比如预订链接、公开的分析 ID、可发布密钥。绝不要用于服务角色密钥、私密密钥、密码或令牌。
不要把真实密钥粘贴进 AI 对话,用https://booking.example.test这样的占位符。也不要把 .env.local 提交到公开仓库。
如果这个值必须在不重新构建的情况下变更,或者必须保密,就不要用 NEXT_PUBLIC_。在服务器代码里读取它,只把浏览器需要的东西传下去,比如通过自己的 API 路由。这个方案没有被测试。
没有验证的部分
其他浏览器(Firefox、Safari)和真实手机或访客;其他 Next.js、React 或 Node 版本,以及 Pages Router;Vercel、Netlify、Cloudflare、Lovable、Bolt 等托管平台在构建时何时何处应用变量——只在本地模拟了"构建时变量缺失";Vite 风格的应用(VITE_ 名称和 import.meta.env);可选链、process.env[name] 和别名的情况,以及"在服务器读取再传下去"的替代方案;真实服务请求拿到 undefined URL 时的行为;Cursor、Lovable、Bolt 或 v0 生成的代码——测试应用是手写的合成应用,没有测量 AI 工具多常产生无前缀的名称;以及那段给 AI 工具的提示词是否能在任何工具里产出正确且安全的 diff。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.