如何配置环境变量
环境变量是配置与代码分离的关键。本文系统讲解环境变量的原理、配置方法、安全策略,以及 Vercel 和 Cloudflare 两大平台的实战操作。
环境变量是什么
环境变量是操作系统和运行时提供的键值对(key-value pair),用于在应用程序外部存储配置信息。它的核心价值在于实现 12-Factor App 第三原则——Config:配置与代码严格分离。
用一个简单的类比:代码是「菜谱」,环境变量是「食材和火候」。同一份菜谱,在不同的厨房(环境)里,食材来源和火候大小各不相同。你不需要为了换一种盐而重写菜谱,只需要告诉厨师「这次用海盐」就够了。
在技术实现上,环境变量分为三种类型:
| 类型 | 说明 | 典型示例 |
|---|---|---|
| 系统变量 | 操作系统预设,所有进程可见 | PATH、HOME、USER |
| 用户变量 | 当前用户会话可见 | 开发时自定义的配置 |
| 运行时变量 | 应用启动时注入 | NODE_ENV、DATABASE_URL、API_KEY |
在 Node.js 中,通过 process.env 读取环境变量:
const dbHost = process.env.DB_HOST // 数据库地址
const apiKey = process.env.STRIPE_API_KEY // 支付 API 密钥
const nodeEnv = process.env.NODE_ENV // 运行环境标识在 Cloudflare Workers 中,访问方式略有不同,通过 env 参数获取:
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const dbHost = env.DB_HOST
const apiKey = env.STRIPE_API_KEY
return new Response(`${dbHost} / ${apiKey}`)
}
}为什么需要环境变量
环境变量解决了三个核心问题:安全性、灵活性和多环境适配。
安全性是最根本的驱动力。API Key、数据库密码、支付密钥等敏感信息绝对不能写死在代码中。即使代码仓库是私有的,一旦有团队成员变更、代码迁移或意外公开,硬编码的密钥就会暴露。环境变量将这些敏感值从代码中剥离,存放在独立的位置,降低了泄露风险。
灵活性体现在无需修改代码就能调整应用行为。数据库地址从开发切到测试,日志级别从 info 调到 debug,第三方服务的端点从 mock 切到真实——这些只需要改变环境变量的值,代码一行都不用动。
多环境适配是持续部署的基础。同一个应用要经历本地开发、自动化测试、预发布验证和正式生产等多个阶段。每个阶段的数据库地址、服务密钥、功能开关各不相同,环境变量让这些差异不需要通过 if-else 写死在代码中,而是通过环境自动注入。
12-Factor App 对此的表述很直接:「配置与代码严格分离。配置项可随环境变化而不需要修改代码。」这不是建议,而是现代应用部署的基本要求。
不同环境的配置策略
一个 AI 产品出海项目通常涉及四个环境:开发(Development)、测试(Test)、预发布(Staging)和生产(Production)。每个环境的配置策略不同。
本地开发环境
本地开发使用 .env 文件管理变量。项目根目录下通常存在多个 .env 文件,按优先级加载:
.env → 默认配置,提交到 Git 作为模板
.env.local → 本地覆盖,不提交到 Git
.env.development → 开发环境专属配置
.env.example 是一个只包含占位符的模板文件,它会提交到 Git,让新加入的团队成员知道需要配置哪些变量:
# .env.example
DATABASE_URL=your_database_url_here
REDIS_URL=redis://localhost:6379
STRIPE_API_KEY=sk_test_your_stripe_key
NEXT_PUBLIC_APP_URL=http://localhost:3000dotenv 库(Node.js 生态中最常用的环境变量加载工具)会在应用启动时自动读取这些文件。Next.js 内置了对 .env 文件的支持,不需要额外配置。
测试环境
测试环境需要与生产隔离的数据库和服务。关键原则是:测试环境绝不能连接生产数据库。通常的做法是在 CI/CD 中为每次测试运行创建临时的数据库实例,测试结束后销毁。
# GitHub Actions 示例
env:
DATABASE_URL: postgresql://test:test@localhost:5432/test_db
REDIS_URL: redis://localhost:6379
NODE_ENV: test预发布环境
预发布环境(Staging)尽可能模拟生产配置,但使用独立的测试密钥和沙箱端点。Stripe 提供了 sk_test_ 前缀的测试密钥,PayPal 有 Sandbox 模式,这些都应该在预发布环境中使用。
生产环境
生产环境的环境变量必须通过平台原生的方式管理,不能使用 .env 文件。Vercel 和 Cloudflare 都提供了 Dashboard 和 CLI 工具来管理生产变量。后文会详细展开。
命名规范
好的命名规范让环境变量的用途一目了然。推荐使用前缀分组策略:
# ✅ 好:前缀清晰,分组明确
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp_production
REDIS_URL=redis://localhost:6379
STRIPE_API_KEY=sk_live_xxxxx
AWS_S3_BUCKET=my-assets
NEXT_PUBLIC_APP_URL=https://example.com
# ❌ 差:含义不明,无法分组
HOST=localhost
KEY=xxxxx
URL=redis://...
SECRET=abc123常用前缀约定:
| 前缀 | 用途 | 示例 |
|---|---|---|
DB_ | 数据库相关 | DB_HOST、DB_PORT、DB_NAME |
REDIS_ | 缓存相关 | REDIS_URL、REDIS_PASSWORD |
STRIPE_ / PAYPAL_ | 支付服务 | STRIPE_API_KEY、STRIPE_WEBHOOK_SECRET |
AWS_ / GCP_ | 云服务 | AWS_S3_BUCKET、GCP_PROJECT_ID |
NEXT_PUBLIC_ | 前端公开变量 | NEXT_PUBLIC_APP_URL(Next.js 约定) |
APP_ | 应用通用配置 | APP_ENV、APP_LOG_LEVEL |
安全存储:环境变量的生命线
环境变量的安全性是整个配置体系的基石。一个被泄露的 API Key 可能导致数据丢失、资金被盗或服务被恶意调用。
第一原则:永远不要提交到 Git
这是不可妥协的底线。.env 文件必须在项目初始化时就加入 .gitignore:
# .gitignore
.env
.env.local
.env.production
*.env.local即使后来从 Git 历史中删除了 .env 文件,它仍然存在于版本历史中。每一个仓库克隆都包含完整的历史记录。一旦密钥进入 Git 历史,唯一正确的做法是立即轮换密钥,而不是试图「清理历史」。
文件权限控制
在本地开发和服务器部署中,.env 文件应该设置为仅当前用户可读写:
chmod 600 .env这防止了同服务器上的其他用户或进程读取你的密钥文件。
启动时验证
应用启动时应该验证所有必需的环境变量是否存在,而不是等到运行时才发现缺失:
// env.ts — 启动时校验
const REQUIRED_VARS = [
'DATABASE_URL',
'REDIS_URL',
'STRIPE_API_KEY',
] as const
function validateEnv() {
const missing = REQUIRED_VARS.filter(key => !process.env[key])
if (missing.length > 0) {
throw new Error(
`Missing required environment variables: ${missing.join(', ')}`
)
}
}
// 在应用入口点调用
validateEnv()这个简单的函数能在应用启动时就暴露配置问题,避免在用户请求到达时才崩溃。
日志脱敏
框架和调试工具可能在启动时打印所有环境变量。确保日志系统配置了脱敏规则,防止密钥进入日志聚合服务(如 Sentry、Datadog):
// ❌ 绝对不要这样做
console.log('Config:', process.env)
console.log('API Key:', process.env.STRIPE_API_KEY)
// ✅ 只记录必要的非敏感信息
console.log('Environment:', process.env.NODE_ENV)
console.log('Database host:', process.env.DB_HOST) // 不含密码密钥轮换策略
长期存在的密钥增加了被泄露后的风险窗口。不同类型密钥的建议生命周期:
| 密钥类型 | 建议最大生命周期 | 轮换方式 |
|---|---|---|
| API Key(第三方服务) | 90 天 | 平台 Dashboard 手动或 API 自动 |
| 数据库密码 | 30 天 | Secret Manager 自动轮换 |
| JWT / Access Token | 分钟到小时级 | 应用自动签发和过期 |
| 部署密钥 | 按次生成 | CI/CD 平台 OIDC 短期 Token |
Secret Manager:超越环境变量
当项目规模增长到多个服务、多个环境、多个团队成员时,单纯的环境变量管理会变得困难。此时需要引入专门的 Secret Manager 工具:
| 工具 | 类型 | 核心特性 | 适用场景 |
|---|---|---|---|
| HashiCorp Vault | 自建 | 动态密钥、细粒度 ACL、审计日志 | 中大型团队、合规要求高 |
| AWS Secrets Manager | AWS 托管 | 自动轮换、IAM 集成 | AWS 技术栈 |
| GCP Secret Manager | GCP 托管 | IAM 绑定、版本管理 | GCP 技术栈 |
| Doppler | SaaS | 跨平台同步、环境管理 | 多平台混合部署 |
| Infisical | 开源自建 | 端到端加密、自托管 | 重视数据主权 |
Vault 的**动态密钥(Dynamic Secrets)**是杀手级特性:为每个应用实例生成唯一的、短期的凭证,用完即弃。这从根本上消除了「长期密钥被盗」的风险。
Vercel 环境变量配置
Vercel 是 Next.js 项目最常用的部署平台,提供了完善的环境变量管理能力。
环境作用域
Vercel 的环境变量支持三个作用域:
| 环境 | 触发条件 | 说明 |
|---|---|---|
| Production | 推送到生产分支(通常 main) | 正式生产环境 |
| Preview | 推送到非生产分支 | 预览部署,支持按分支覆盖 |
| Development | 本地 vercel dev 或 vercel env pull | 本地开发,值存入 .env.local |
三种配置方式
方式一:Dashboard 配置
进入 Vercel Dashboard → 项目 → Settings → Environment Variables,手动添加变量。这是最直观的方式,适合少量变量的初始配置。
方式二:CLI 配置
# 添加变量(交互式)
vercel env add STRIPE_API_KEY production
# 拉取变量到本地
vercel env pull .env.local
# 列出所有变量
vercel env ls方式三:API 配置
# 通过 cURL 批量配置
curl --request POST \
--url https://api.vercel.com/v10/projects/<project-id>/env \
--header "Authorization: Bearer $VERCEL_TOKEN" \
--data '[{
"key": "STRIPE_API_KEY",
"value": "sk_live_xxxxx",
"target": ["production"]
}]'敏感变量(Sensitive Variables)
Vercel 提供了敏感变量的特殊标记,一旦创建就无法再读取值,只能覆盖:
# 创建敏感变量
curl --request POST \
--url https://api.vercel.com/v10/projects/<project-id>/env \
--header "Authorization: Bearer $VERCEL_TOKEN" \
--data '[{
"key": "STRIPE_SECRET",
"value": "sk_live_xxxxx",
"type": "sensitive",
"target": ["production"]
}]'敏感变量的特性:
| 特性 | 说明 |
|---|---|
| 创建后不可读 | Dashboard 和 API 都无法查看原始值 |
| 构建日志脱敏 | 长度 ≥ 32 字符的值自动替换为 [REDACTED] |
| 仅支持 Production / Preview | Development 环境不支持敏感变量标记 |
| 团队级策略 | 可强制所有新建变量必须为 Sensitive |
容量限制
Vercel 对环境变量有明确的容量限制:
- 每个部署最多 64 KB 的环境变量总量
- Edge Functions 和 Middleware(
edgeruntime)单个变量限制 5 KB
如果你的配置超过这些限制,需要重新评估是否有不必要的变量,或者考虑将配置拆分到外部服务。
系统自动注入变量
Vercel 会自动注入以下系统变量,无需手动配置:
| 变量名 | 说明 |
|---|---|
VERCEL_URL | 当前部署的 URL |
VERCEL_BRANCH | Git 分支名 |
VERCEL_ENV | 当前环境(production / preview / development) |
VERCEL_AUTOMATION_BYPASS_SECRET | 绕过自动化检测的密钥(始终脱敏) |
Cloudflare 环境变量配置
Cloudflare Workers 和 Pages 的环境变量模型与 Vercel 有显著差异,需要特别注意。
Workers 环境变量
Workers 的环境变量分为两类:普通变量(vars)和加密密钥(secrets)。
普通变量通过 wrangler.toml 配置,明文存储,Dashboard 可见:
# wrangler.toml
[vars]
MY_VAR = "plain-text-value"
API_ENDPOINT = "https://api.example.com"加密密钥通过 CLI 设置,加密存储,Dashboard 不可查看原始值:
# 设置加密密钥(交互式输入)
wrangler secret put STRIPE_API_KEY
# 列出已设置的密钥(只显示名称,不显示值)
wrangler secret list
# 删除密钥
wrangler secret delete STRIPE_API_KEY在 Worker 代码中,通过 env 参数访问:
// 定义类型
interface Env {
MY_VAR: string // 普通变量
STRIPE_API_KEY: string // 加密密钥
DB: D1Database // D1 数据库绑定
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
// 普通变量和加密密钥通过相同方式访问
const endpoint = env.API_ENDPOINT
const apiKey = env.STRIPE_API_KEY
return new Response(`Endpoint: ${endpoint}`)
}
}本地开发配置
Cloudflare 本地开发使用 .dev.vars 文件(类似 .env.local),这个文件不提交到 Git:
# .dev.vars — 不提交到 Git
STRIPE_API_KEY=sk_test_xxxxx
DATABASE_URL=postgresql://localhost:5432/dev_dbwrangler dev 启动时会自动加载 .dev.vars 中的变量。
多环境配置
Cloudflare 通过 wrangler.toml 的环境(env)配置实现多环境隔离:
# wrangler.toml
name = "my-worker"
[vars]
API_ENDPOINT = "https://api.example.com"
# 预发布环境
[env.staging]
name = "my-worker-staging"
[env.staging.vars]
API_ENDPOINT = "https://staging-api.example.com"
# 生产环境
[env.production]
name = "my-worker"
[env.production.vars]
API_ENDPOINT = "https://prod-api.example.com"平台对比:Vercel vs Cloudflare
两个平台的环境变量模型有本质区别,理解这些差异对于选择部署平台和迁移方案至关重要。
| 维度 | Vercel | Cloudflare Workers |
|---|---|---|
| 访问方式 | process.env.MY_VAR | env.MY_VAR(fetch handler 参数) |
| 敏感变量 | Dashboard 标记 Sensitive | wrangler secret put |
| 加密存储 | Sensitive 变量静态加密 | Secrets 加密,vars 明文 |
| 本地开发 | .env.local + vercel dev | .dev.vars + wrangler dev |
| 环境隔离 | Production / Preview / Development | 通过 wrangler env 配置 |
| 容量限制 | 64 KB / 部署,Edge 5 KB / 变量 | 无明确文档限制 |
| 系统变量 | 自动注入 VERCEL_* | 通过 Bindings 注入 |
| 从 Vercel 迁移注意 | — | process.env 在 Worker 中不可用(除非启用 Node.js 兼容) |
环境变量配置流程
以下 Mermaid 流程图展示了从开发到生产的完整环境变量配置流程:
实战案例
案例一:AI 产品的 Stripe 支付集成
一个面向海外用户的 AI 写作工具需要集成 Stripe 进行订阅收费。这个项目需要在多个环境中管理 Stripe 相关的密钥。
开发环境使用 Stripe 测试密钥,连接本地数据库:
# .env.local(不提交到 Git)
STRIPE_API_KEY=sk_test_4eC39HqLyjWDarjtT1zdp7dc
STRIPE_WEBHOOK_SECRET=whsec_test_xxxxx
DATABASE_URL=postgresql://localhost:5432/ai_writer_dev
NEXT_PUBLIC_APP_URL=http://localhost:3000Vercel 生产环境使用 Stripe 正式密钥,通过 Dashboard 配置并标记为 Sensitive:
STRIPE_API_KEY=sk_live_xxxxx → Sensitive, Production only
STRIPE_WEBHOOK_SECRET=whsec_xxxxx → Sensitive, Production only
DATABASE_URL=postgresql://prod-db... → Sensitive, Production + Preview
NEXT_PUBLIC_APP_URL=https://aiwriter.com → 非敏感, 所有环境
启动验证确保生产环境不会因缺少密钥而静默失败:
// lib/env.ts
const requiredInProduction = [
'STRIPE_API_KEY',
'STRIPE_WEBHOOK_SECRET',
'DATABASE_URL',
] as const
export function validateProductionEnv() {
if (process.env.NODE_ENV !== 'production') return
const missing = requiredInProduction.filter(key => !process.env[key])
if (missing.length > 0) {
throw new Error(`Production env missing: ${missing.join(', ')}`)
}
}
// app/layout.tsx 或 server.ts 入口调用
validateProductionEnv()案例二:从 Vercel 迁移到 Cloudflare Workers
一个团队决定将 AI 产品的 API 层从 Vercel Serverless Functions 迁移到 Cloudflare Workers,以获得更好的边缘计算性能和成本控制。
迁移过程中遇到的核心问题是环境变量的访问方式完全不同。在 Vercel 中,代码通过 process.env 读取变量;在 Cloudflare Workers 中,需要通过 env 参数传递。
迁移前的 Vercel 代码:
// api/stripe.ts (Vercel Serverless Function)
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_API_KEY!)
export default async function handler(req: Request) {
const session = await stripe.checkout.sessions.create({ /* ... */ })
return Response.json(session)
}迁移后的 Cloudflare Worker 代码:
// worker.ts (Cloudflare Worker)
interface Env {
STRIPE_API_KEY: string
}
export default {
async fetch(request: Request, env: Env) {
const stripe = new Stripe(env.STRIPE_API_KEY)
const session = await stripe.checkout.sessions.create({ /* ... */ })
return Response.json(session)
}
}关键差异在于:
- 访问方式:
process.env.KEY→env.KEY - 类型定义:需要手动定义
Env接口 - 密钥设置:Vercel Dashboard →
wrangler secret put - 本地开发:
.env.local→.dev.vars
团队通过渐进式迁移完成了过渡:先在 Cloudflare 上搭建并行环境,用相同的业务逻辑但不同的环境变量配置运行,验证通过后再切换流量。
检查清单
在部署 AI 产品前,逐项确认以下环境变量配置是否到位:
-
.env文件已加入.gitignore,项目初始化时就配置好 -
.env.example模板已提交到 Git,包含所有必需变量的占位符 - 本地开发使用
.env.local,不提交到 Git - 所有环境变量使用有意义的前缀分组命名(
DB_、REDIS_、STRIPE_) - 生产环境通过平台原生方式管理变量(Vercel Dashboard / wrangler secret)
- 敏感变量(API Key、密码)已标记为加密存储
- 不同环境使用不同的密钥(开发用测试密钥,生产用正式密钥)
- 应用启动时验证所有必需的环境变量是否存在
- 日志系统已配置脱敏规则,防止密钥进入日志
- 代码中不存在
console.log(process.env)等调试残留 - 密钥定义了最大生命周期,有轮换计划
- 密钥泄露的应急流程已文档化(发现泄露 → 轮换 → 审计影响范围)
- CI/CD 中的密钥通过平台 Secrets 管理,不在 YAML 中硬编码
- Pre-commit hook 配置了密钥检测工具(如 gitleaks、detect-secrets)
- 迁移或平台变更时,已确认环境变量的访问方式差异
参考资料
- The Twelve-Factor App — Config — 环境变量设计的理论基础
- Vercel Environment Variables — Vercel 官方文档
- Vercel Sensitive Environment Variables — Vercel 敏感变量配置
- Cloudflare Workers Environment Variables — Cloudflare 官方文档
- OWASP Secrets Management Cheat Sheet — OWASP 密钥管理指南
- Environment Variables: How to Use Them and 4 Critical Best Practices — Configu 最佳实践
- Best Practices for Environment Variables Secrets Management — GitGuardian 安全管理指南
- Storing Secrets in Env Vars Considered Harmful — Arcjet 环境变量安全分析