Next.js 环境变量实战:NEXT_PUBLIC 前缀、构建时 vs 运行时与密钥泄露排查

发布时间:2026/7/22 23:09:53

Next.js 环境变量实战:NEXT_PUBLIC 前缀、构建时 vs 运行时与密钥泄露排查
Next.js 环境变量实战:NEXT_PUBLIC 前缀、构建时 vs 运行时与密钥泄露排查「我在.env里配了API_KEY,前端process.env.API_KEY却是 undefined」「本地好好的,部署到服务器变量全丢了」「更可怕的是,我的密钥居然出现在浏览器打包文件里」。Next.js 的环境变量踩坑率极高,因为它同时跨了服务端和客户端两个世界,规则和纯前端项目完全不同。这篇按真实排查顺序讲清楚。第一个坑:客户端读不到变量新建.env.local:API_KEYsk-secret-123NEXT_PUBLIC_SITE_NAME我的站点在一个客户端组件里读:use client; export default function Header() { // API_KEY 是 undefined,NEXT_PUBLIC_SITE_NAME 正常 console.log(process.env.API_KEY); // undefined console.log(process.env.NEXT_PUBLIC_SITE_NAME); // 我的站点 return h1{process.env.NEXT_PUBLIC_SITE_NAME}/h1; }这不是 bug,是 Next.js 的刻意设计:只有以NEXT_PUBLIC_开头的变量才会被打进客户端 bundle,其余变量只在服务端可见。原因很直接:客户端代码会下载到用户浏览器。如果所有变量都注入进去,你的数据库密码、第三方 API 密钥就全裸奔了。Next.js 用前缀强制你显式声明「这个变量我确认可以公开」。所以规则是:密钥类(数据库连接串、API secret、token):不加前缀,只在服务端组件 / Route Handler / Server Action 里用。公开配置(站点名、公开的分析 ID、公开 API 地址):加NEXT_PUBLIC_前缀,客户端才能读。第二个坑:NEXT_PUBLIC 是构建时「写死」的,不是运行时读的这个坑更隐蔽。很多人以为环境变量是程序运行时去读的,对NEXT_PUBLIC_变量来说不是——它们在next build那一刻就被字面替换进代码了。看编译前:const name process.env.NEXT_PUBLIC_SITE_NAME;next build之后,bundle 里实际是:constname我的站点;// 已经被替换成字面量字符串这带来一个致命后果:构建完成后再改环境变量,NEXT_PUBLIC_的值不会变。典型翻车场景:用 Docker 打了一个镜像,想在测试/生产环境用不同的NEXT_PUBLIC_API_URL——做不到,因为值在build时已经烤进镜像了。CI 里 build 时忘了设某个NEXT_PUBLIC_变量,结果它变成 undefined 烤进产物,线上怎么改服务器环境变量都没用。服务端变量则不同,它们是运行时读取的:// 服务端组件 / Route Handler,运行时读取,改了重启就生效 export async function GET() { const key process.env.API_KEY; // 运行时才求值 const res await fetch(https://api.example.com/data, { headers: { Authorization: Bearer ${key} }, }); return Response.json(await res.json()); }记忆口诀:NEXT_PUBLIC_ 构建时快照,服务端变量 运行时读取。要在多环境复用同一个镜像,公开配置就别用NEXT_PUBLIC_硬编,改用「运行时通过服务端接口下发配置」的方式(下面讲)。第三个坑:文件加载优先级和 .gitignoreNext.js 会按固定顺序加载多个 env 文件,后加载的不会覆盖已存在的同名变量(先到先得):.env.local # 最高优先级,本地专用,绝不提交 .env.development # next dev 时加载 .env.production # next build / next start 时加载 .env # 兜底默认值实战约定:.env:提交到仓库,放非敏感的默认值(如默认端口)。.env.local:写进.gitignore,放本地密钥,永远不提交。生产密钥:走部署平台(Vercel/K8s Secret/CI 变量),不落文件。确认.gitignore里有这行(Next.js 脚手架默认会加,但手搭项目常漏):# .gitignore.env*.local排查:密钥是不是泄露进了客户端?改完之后,一定要验证密钥没被打进前端。两个办法:方法一,build 后全局搜产物:next build# 在构建产物里搜你的密钥值,应该 0 命中grep-rsk-secret-123.next/static只要.next/static(客户端产物目录)里搜到密钥,就说明它被泄露了——大概率是你在客户端组件里读了非NEXT_PUBLIC_变量,或误加了前缀。方法二,浏览器 Network 面板看 JS chunk 内容,直接搜密钥字符串。一个常见的泄露写法是把服务端数据「透传」给客户端组件时连密钥一起传了:// 危险:整个 config 对象带着密钥传给了客户端组件 const config { apiKey: process.env.API_KEY, siteName: ... }; return ClientWidget config{config} /; // apiKey 会出现在 HTML 里!正确做法是只挑能公开的字段传:// 只传公开字段,密钥留在服务端 return ClientWidget siteName{process.env.NEXT_PUBLIC_SITE_NAME} /;进阶:运行时下发公开配置(解决多环境复用镜像)如果你确实要「一次构建、多环境部署」,又需要客户端拿到不同的公开配置,别用NEXT_PUBLIC_。改成客户端向自己的服务端接口请求配置,服务端运行时读环境变量返回:// app/api/config/route.ts —— 运行时读,改环境变量重启即生效 export async function GET() { return Response.json({ apiUrl: process.env.PUBLIC_API_URL, // 注意:没有 NEXT_PUBLIC_ 前缀 siteName: process.env.SITE_NAME, }); }use client; import { useEffect, useState } from react; export function useRuntimeConfig() { const [cfg, setCfg] useState{ apiUrl: string } | null(null); useEffect(() { // 客户端运行时拉取,值取决于当前环境的服务端变量,而非构建快照 fetch(/api/config).then(r r.json()).then(setCfg); }, []); return cfg; }这样同一个镜像丢到 test / prod,配置由各环境的运行时变量决定,不用为每个环境重新 build。代价是多一次请求 客户端初始没有配置的一小段空窗,按需取舍。小结NEXT_PUBLIC_前缀才会进客户端 bundle;没前缀的变量只在服务端可见,这是防密钥泄露的机制,别为了「读得到」乱加前缀。NEXT_PUBLIC_是构建时字面替换,build 后改不了;服务端变量是运行时读取,重启即生效。要多环境复用镜像,公开配置走运行时接口下发。文件优先级:.env.local.env.development/.env.production.env,先到先得;.env*.local必须进.gitignore。上线前排查:grep一下.next/static里有没有密钥,0 命中才安全。记忆点:Next.js 环境变量的一切困惑,都来自「这行代码到底跑在服务端还是客户端、值是在 build 时定的还是运行时读的」——先想清楚这两问,坑就绕开了。

相关新闻

IT6113:高速MIPI DSI 4通道转8车通道

IT6113:高速MIPI DSI 4通道转8车通道

2026/7/22 23:09:53

IT6113 MIPI DSI 视频桥是一种 4 通道输入、8 通道输出的高速数据转换器/分路器。它集成了4通道单链路MIPI DSI接收器和8通道双链路MIPI DSI发射机。DSI桥可以配置为半速率数据转换器或全速率数据分配器,以适应不同应用。作为半速率数据转换器时,IT6113的…

Qt 高级编程 036:QSS自定义控件之道

Qt 高级编程 036:QSS自定义控件之道

2026/7/22 23:09:53

Qt 高级编程 036:QSS自定义控件之道✨🌟✨ 序章 缘起 ✨🌟✨Bilibili 同步视频✨📖✨ 第一章 基础入门 初窥门径 ✨📖✨🎯 1.1 基础定义 何谓QSS⚙️ 1.2 配置入口 入门之径🌳 1.3 作用范围…

OpenSCAD:程序员如何用代码实现3D参数化建模

OpenSCAD:程序员如何用代码实现3D参数化建模

2026/7/22 22:59:53

1. OpenSCAD:程序员专属的3D建模利器第一次接触OpenSCAD时,我正为一个自动化测试夹具设计发愁。当时主流的3D建模软件界面复杂,每次修改尺寸都要反复点击菜单。直到同事推荐了这个"用代码建模"的工具,我的工作流彻底改变…

VirtualBox主机与虚拟机复制粘贴失效问题解决方案

VirtualBox主机与虚拟机复制粘贴失效问题解决方案

2026/7/23 3:40:09

1. VirtualBox主机与虚拟机复制粘贴失效问题解析VirtualBox作为一款开源的虚拟化软件,在日常开发和测试中被广泛使用。但很多用户都会遇到一个典型问题:主机和虚拟机之间无法正常使用复制粘贴功能。这种情况尤其常见在安装完新系统后,或是升级…

C语言指针深入 I 学习总结

C语言指针深入 I 学习总结

2026/7/23 3:40:09

目录 1.const修饰指针 2.野指针 3.assert断言 4.传值调用和传址调用 一.const修饰指针 例1.const修饰局部变量a,a不能够被赋值; const修饰局部变量a,a 是常变量:本质依旧是变量,拥有内存空间,但是通过…

支付风控的“AlphaGo时刻 ——Data Agent驱动的三层AI风控架构

支付风控的“AlphaGo时刻 ——Data Agent驱动的三层AI风控架构

2026/7/23 3:40:09

01|规则写到400 条时,我开始怀疑这条路本身 我是上海富友支付服务股份有限公司的技术负责人。富友是一家科技驱动型的支付公司,先后获得由中国人民银行颁发的多项支付业务资质,也是上海市高新技术企业、上海市重点软件企业、上海…

PDF表格数据提取:Camelot与Tabula实战指南

PDF表格数据提取:Camelot与Tabula实战指南

2026/7/23 3:40:09

1. 为什么我们需要专业PDF表格提取工具在处理PDF文档时,最令人头疼的莫过于需要从中提取表格数据。我曾参与过一个金融数据分析项目,客户提供了300多份PDF格式的季度报表,每份包含5-6个关键数据表。最初尝试手动复制粘贴,不仅效率…

Google早期技术决策与工程师文化:从搜索基础设施到规模化实践

Google早期技术决策与工程师文化:从搜索基础设施到规模化实践

2026/7/23 3:40:09

这次我们来看一个特殊的项目——不是技术工具,而是一段珍贵的历史记录。一位前 Google 员工回忆了公司在 2000 年代初期的创业氛围、技术文化和工作日常。对于今天想了解硅谷技术公司早期发展、工程师文化形成,或者单纯对 Google 成长史感兴趣的读者&…

基于 NFS 与 autofs 实现 Linux 多节点存储分离实战指南

基于 NFS 与 autofs 实现 Linux 多节点存储分离实战指南

2026/7/23 3:30:09

二.利用nfs实现存储分离 NFS 存储分离的核心概念 NFS(Network File System)是一种分布式文件系统协议,允许客户端通过网络访问远程服务器上的文件,实现存储与计算资源的分离。其核心目标是将存储集中化管理,同时为多台…

微服务进阶:服务网格与Istio

微服务进阶:服务网格与Istio

2026/7/23 3:40:08

541|微服务进阶:服务网格与Istio 上篇文章我们聊了微服务的基本概念和拆分方法。 但微服务多了,问题也多了: 服务之间怎么通信? 怎么监控每个服务的调用链路? 熔断、限流、重试怎么做? 安全认证怎么统一? 以前这些都靠SDK库(比如Hystrix、Feign),每个服务都要集成…

零售超级终端全域协同:ShareKit 碰一碰商品流转业务落地案例

零售超级终端全域协同:ShareKit 碰一碰商品流转业务落地案例

2026/7/21 9:56:14

一、零售门店全域协同业务背景与行业痛点 1.1 门店超级终端设备矩阵(连锁便利店/商超标准配置) 自助收银Kiosk一体机:顾客结算、自助核销优惠券、商品素材预览;运营折叠平板:店长后台商品上新、图片录入、活动配置、…

噗叽短视频界面分析

噗叽短视频界面分析

2026/7/23 1:54:13

1 和小红书类似,可以采用类似判断方法------------其实他比小红书好判断,因为他没有图片,控件位置几乎是固定的,都不用判断------------2 因为他没有点赞按钮------------而且几乎所有控件位置都是完全一样的,所以我就…

企业级AI搜索落地选型实战手册(含LLM+RAG+Hybrid架构对比矩阵与ROI测算模板)

企业级AI搜索落地选型实战手册(含LLM+RAG+Hybrid架构对比矩阵与ROI测算模板)

2026/7/23 0:09:56

更多请点击: https://kaifayun.com 第一章:企业级AI搜索落地选型实战手册(含LLMRAGHybrid架构对比矩阵与ROI测算模板) 企业级AI搜索系统落地成败,核心在于技术选型与业务价值的精准对齐。盲目堆砌大模型能力或过度依赖…

TM4C129LNCZAD外设实战:LCD、比较器与PWM寄存器配置详解

TM4C129LNCZAD外设实战:LCD、比较器与PWM寄存器配置详解

2026/7/23 0:09:56

1. 项目概述与核心价值在嵌入式系统开发,尤其是基于ARM Cortex-M内核的微控制器项目中,深入理解并熟练配置芯片的片上外设,是从“点亮LED”迈向“实现复杂系统功能”的关键一步。Tiva™ TM4C129LNCZAD作为TI公司Cortex-M4F家族中的高性能成员…

AtomCode `fmt_dur` 争议溯源:两个函数、三段演进、四个事实

AtomCode `fmt_dur` 争议溯源:两个函数、三段演进、四个事实

2026/7/23 0:09:56

一、快速声明与争议背景本文是对 AtomCode 终端 spinner 时长显示 fmt_dur 相关说法的事实性核验。2026 年 7 月 CSDN 上出现两篇互相矛盾的博文,近期又有 AI 在对话中输出格式描述 XhYm / YmZs / Zs。本文基于 AtomCode 仓库 main4677ddfa 及全分支 Git 历史给出可…