# Next.js 中 NEXT_PUBLIC_ 環境變數的 Build-time 陷阱 在使用 Next.js 進行開發時,許多人會直覺地認為,只要修改 `.env` 檔案或系統環境變數,前端程式碼就能在部署後即時反映最新設定。然而,當應用程式完成正式建置並部署至 Production 環境後,往往會發現一件令人困惑的事情──**以 `NEXT_PUBLIC_` 開頭的環境變數並不會隨環境變化而更新**。 這個行為在開發階段不易察覺,但一旦進入正式環境,便很容易成為實際踩到的坑。 --- ## 原因:NEXT_PUBLIC_ 會在建置階段被「寫死」 根據官方文件 [Bundling Environment Variables for the Browser](https://nextjs.org/docs/pages/guides/environment-variables#bundling-environment-variables-for-the-browser),Next.js 在建置(build)階段,會將所有以 `NEXT_PUBLIC_` 開頭的環境變數**直接內嵌(inline)到前端 bundle 中** 這代表: - 以 `NEXT_PUBLIC_` 開頭的變數,一旦完成建置,無論部署到哪個環境,這些值都不會再改變 ## 解決方法 如果確實有需求在前端取得**執行時(runtime)**的環境變數,一個可行且穩定的做法是: 1. 在 **Server Component**(例如 Root Layout)中讀取 `process.env` 2. 將變數透過 `<Script>` 注入到瀏覽器的 `window` 物件 3. 由前端程式碼在 runtime 讀取這些值 這樣即可避開 Next.js 在建置階段對 `NEXT_PUBLIC_` 的靜態替換行為。 ### Layout 範例 ```tsx import Script from "next/script"; export default async function RootLayout({ children, params, }: { children: React.ReactNode; params: Promise<{ lng: string }>; }) { const { lng } = await params; // 使用動態索引方式,避免在 build-time 被靜態替換 const runtimeUrl = process.env["NEXT_PUBLIC_APP_URL"]; return ( <html lang={lng}> <body> <Script id="env-script" strategy="beforeInteractive"> {`window.ENV = { NEXT_PUBLIC_APP_URL: ${JSON.stringify(runtimeUrl)} };`} </Script> {children} </body> </html> ); } ``` 這樣在前端任何的地方都可以透過 `window.ENV.NEXT_PUBLIC_APP_URL` 來取得動態變數 ### Hono Client 範例 ```TypeScript import { hc } from "hono/client"; import type { HonoAppRoute } from "@/app/api/[...route]/route"; const getBaseUrl = () => { if (typeof window !== "undefined" && (window as any).ENV?.NEXT_PUBLIC_APP_URL) { return (window as any).ENV.NEXT_PUBLIC_APP_URL; } return process.env.NEXT_PUBLIC_APP_URL || ""; }; const client = hc<HonoAppRoute>(`${getBaseUrl()}/`, { init: { credentials: "include", }, }); export { client as apiClient }; ```