从0 死磕全栈之Next.js 环境变量实战指南:企业级多环境(dev/test/prod)配置最佳实践

一、Next.js 环境变量机制速览

Next.js 内置对 .env 文件的支持,但其行为有关键安全边界

变量前缀 是否暴露给客户端 构建时内联 运行时可变
NEXT_PUBLIC_ ✅ 是 ✅ 是 ❌ 否(构建后冻结)
无前缀(如 DB_URL ❌ 否 ❌ 否 ✅ 是(仅服务端)

⚠️ 重要原则
永远不要将密钥、数据库地址、内部 API 以 NEXT_PUBLIC_ 开头!


二、企业级多环境目录结构

python 复制代码
your-nextjs-app/
├── .env.local               # ← 本地开发(gitignore)
├── .env.development         # ← 开发环境默认值(可提交)
├── .env.test                # ← 测试环境默认值(可提交)
├── .env.production          # ← 生产环境默认值(可提交)
├── src/
│   ├── lib/
│   │   └── env.ts           # ← 统一加载 & 类型校验
│   └── app/
└── next.config.js

Git 提交策略

  • .env.*(无 .local 后缀)→ 提交到仓库(含安全默认值)
  • .env.local加入 .gitignore(含个人/敏感密钥)

三、分环境配置实战代码

1. 定义各环境 .env 文件

.env.development(开发默认)

env 复制代码
# 公共配置(客户端可用)
NEXT_PUBLIC_APP_NAME=MyApp Dev
NEXT_PUBLIC_API_BASE=/api

# 服务端配置(仅服务端)
DB_URL=postgresql://dev:dev@localhost:5432/myapp_dev
AUTH_SECRET=dev-secret-change-in-prod
LOG_LEVEL=debug

.env.test(测试环境)

env 复制代码
NEXT_PUBLIC_APP_NAME=MyApp Test
NEXT_PUBLIC_API_BASE=https://api.test.myapp.com

DB_URL=postgresql://test:test@test-db:5432/myapp_test
AUTH_SECRET=test-secret-12345
LOG_LEVEL=info

.env.production(生产环境)

env 复制代码
NEXT_PUBLIC_APP_NAME=MyApp
NEXT_PUBLIC_API_BASE=https://api.myapp.com

# 注意:DB_URL 和 AUTH_SECRET 实际由 CI/CD 注入,此处仅为类型占位
DB_URL=***OVERRIDE_IN_DEPLOYMENT***
AUTH_SECRET=***OVERRIDE_IN_DEPLOYMENT***
LOG_LEVEL=warn

💡 提示 :生产环境的敏感值通常由部署平台(Vercel/Docker/K8s)注入,.env.production 仅用于类型提示和本地模拟。


2. 统一加载与类型校验(src/lib/env.ts

ts 复制代码
// src/lib/env.ts
import { z } from 'zod'

// 定义环境变量 Schema
const EnvSchema = z.object({
  // 客户端变量(NEXT_PUBLIC_)
  NEXT_PUBLIC_APP_NAME: z.string(),
  NEXT_PUBLIC_API_BASE: z.string().url(),

  // 服务端变量
  DB_URL: z.string().url(),
  AUTH_SECRET: z.string().min(32),
  LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']),
})

// 加载并校验
let env: z.infer<typeof EnvSchema>

try {
  env = EnvSchema.parse(process.env)
} catch (error) {
  console.error('❌ Invalid environment variables:', error)
  process.exit(1)
}

export { env }

优势

  • 启动时校验缺失/格式错误
  • 提供完整 TypeScript 类型提示
  • 避免散落在各处的 process.env.XXX

3. 在 App Router 中使用

服务端组件(安全访问所有变量)

tsx 复制代码
// src/app/page.tsx
import { env } from '@/lib/env'
import { sql } from '@/lib/db'

export default async function HomePage() {
  // ✅ 安全:DB_URL 仅在服务端可用
  const [userCount] = await sql`SELECT COUNT(*) FROM users`
  
  return (
    <div>
      <h1>{env.NEXT_PUBLIC_APP_NAME}</h1>
      <p>User count: {userCount.count}</p>
    </div>
  )
}

客户端组件(仅能访问 NEXT_PUBLIC_)

tsx 复制代码
// src/app/ui/Header.tsx
'use client'

import { env } from '@/lib/env' // ← 会报错!env 含服务端变量

// ✅ 正确做法:只导入公共变量
const { NEXT_PUBLIC_APP_NAME, NEXT_PUBLIC_API_BASE } = process.env

export default function Header() {
  return <header>{NEXT_PUBLIC_APP_NAME}</header>
}

🔒 安全加固 :在 src/lib/env.ts 中拆分客户端/服务端对象:

ts 复制代码
export const publicEnv = {
  NEXT_PUBLIC_APP_NAME: env.NEXT_PUBLIC_APP_NAME,
  NEXT_PUBLIC_API_BASE: env.NEXT_PUBLIC_API_BASE,
}

四、测试环境特殊处理

Next.js 不会自动加载 .env.test,需手动指定:

方案 1:运行测试时显式设置 NODE_ENV=test

bash 复制代码
# package.json
{
  "scripts": {
    "test": "NODE_ENV=test jest",
    "test:e2e": "NODE_ENV=test playwright test"
  }
}

方案 2:在测试 setup 中加载(推荐)

ts 复制代码
// test/setup-env.ts
import { loadEnvConfig } from '@next/env'

// 加载 .env.test
loadEnvConfig(process.cwd(), false, { info: () => {}, error: console.error })

然后在 Jest/Playwright 配置中引入:

js 复制代码
// jest.config.js
module.exports = {
  setupFiles: ['./test/setup-env.ts'],
}

✅ 效果:测试时 process.env 自动包含 .env.test 内容。


五、Docker 多阶段部署示例

dockerfile 复制代码
# Dockerfile
FROM node:20-alpine AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM base AS builder
COPY . .
# 构建时注入公共变量(注意:非敏感!)
ARG NEXT_PUBLIC_APP_NAME
ARG NEXT_PUBLIC_API_BASE
ENV NEXT_PUBLIC_APP_NAME=$NEXT_PUBLIC_APP_NAME
ENV NEXT_PUBLIC_API_BASE=$NEXT_PUBLIC_API_BASE
RUN npm run build

FROM base AS runner
# 运行时注入敏感变量(通过 docker run -e)
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

EXPOSE 3000
CMD ["node", "server.js"]

部署命令:

bash 复制代码
# 构建(仅传公共变量)
docker build \
  --build-arg NEXT_PUBLIC_APP_NAME="MyApp" \
  --build-arg NEXT_PUBLIC_API_BASE="https://api.myapp.com" \
  -t myapp .

# 运行(传敏感变量)
docker run -d \
  -e DB_URL="postgresql://prod:xxx@prod-db/..." \
  -e AUTH_SECRET="very-long-secret-here" \
  -p 3000:3000 \
  myapp

安全分离:构建镜像不含密钥,密钥仅在运行时注入。


六、常见陷阱与解决方案

问题 原因 解决方案
process.env.DB_URL 在客户端为 undefined 未加 NEXT_PUBLIC_ 正确:服务端变量绝不暴露给客户端
生产环境变量未更新 NEXT_PUBLIC_ 在构建时冻结 敏感配置走 API 获取或服务端注入
测试时连错数据库 未加载 .env.test 使用 loadEnvConfig 或设 NODE_ENV=test
.env.local 被误提交 未加 .gitignore 立即加入:echo ".env.local" >> .gitignore

结语:环境变量是信任边界

在 Next.js 的混合渲染模型中,环境变量不仅是配置,更是安全边界的体现。通过分层设计(公共/私有)、类型校验、多环境隔离,我们既能享受开发便利,又能守住安全底线。


相关推荐
.生产的驴7 小时前
React 集成Redux数据状态管理 数据共享 全局共享
前端·javascript·react.js·前端框架·css3·html5·safari
IT_陈寒7 小时前
Redis性能优化的7个隐藏技巧:从慢查询到亿级QPS的实战经验分享
前端·人工智能·后端
艾小码7 小时前
ES6+革命:8大特性让你的JavaScript代码质量翻倍
前端·javascript
两个西柚呀7 小时前
Vue组件的一些底层细节
前端·javascript·vue.js
IT技术分享社区7 小时前
前端:浏览器Content Security Policy 安全策略介绍和用法
前端·前端开发
林强18111 小时前
前端文件预览docx、pptx和xlsx
前端
像是套了虚弱散13 小时前
DevEco Studio与Web联合开发:打造鸿蒙混合应用的全景指南
开发语言·前端·华为·harmonyos·鸿蒙
衬衫chenshan14 小时前
【CTF】强网杯2025 Web题目writeup
前端
飞翔的佩奇14 小时前
【完整源码+数据集+部署教程】【天线&水】舰船战舰检测与分类图像分割系统源码&数据集全套:改进yolo11-repvit
前端·python·yolo·计算机视觉·数据集·yolo11·舰船战舰检测与分类图像分割系统