如何配置环境变量

环境变量是配置与代码分离的关键。本文系统讲解环境变量的原理、配置方法、安全策略,以及 Vercel 和 Cloudflare 两大平台的实战操作。

环境变量是什么

环境变量是操作系统和运行时提供的键值对(key-value pair),用于在应用程序外部存储配置信息。它的核心价值在于实现 12-Factor App 第三原则——Config:配置与代码严格分离。

用一个简单的类比:代码是「菜谱」,环境变量是「食材和火候」。同一份菜谱,在不同的厨房(环境)里,食材来源和火候大小各不相同。你不需要为了换一种盐而重写菜谱,只需要告诉厨师「这次用海盐」就够了。

在技术实现上,环境变量分为三种类型:

类型说明典型示例
系统变量操作系统预设,所有进程可见PATHHOMEUSER
用户变量当前用户会话可见开发时自定义的配置
运行时变量应用启动时注入NODE_ENVDATABASE_URLAPI_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:3000

dotenv 库(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_HOSTDB_PORTDB_NAME
REDIS_缓存相关REDIS_URLREDIS_PASSWORD
STRIPE_ / PAYPAL_支付服务STRIPE_API_KEYSTRIPE_WEBHOOK_SECRET
AWS_ / GCP_云服务AWS_S3_BUCKETGCP_PROJECT_ID
NEXT_PUBLIC_前端公开变量NEXT_PUBLIC_APP_URL(Next.js 约定)
APP_应用通用配置APP_ENVAPP_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 ManagerAWS 托管自动轮换、IAM 集成AWS 技术栈
GCP Secret ManagerGCP 托管IAM 绑定、版本管理GCP 技术栈
DopplerSaaS跨平台同步、环境管理多平台混合部署
Infisical开源自建端到端加密、自托管重视数据主权

Vault 的**动态密钥(Dynamic Secrets)**是杀手级特性:为每个应用实例生成唯一的、短期的凭证,用完即弃。这从根本上消除了「长期密钥被盗」的风险。

Vercel 环境变量配置

Vercel 是 Next.js 项目最常用的部署平台,提供了完善的环境变量管理能力。

环境作用域

Vercel 的环境变量支持三个作用域:

环境触发条件说明
Production推送到生产分支(通常 main正式生产环境
Preview推送到非生产分支预览部署,支持按分支覆盖
Development本地 vercel devvercel 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 / PreviewDevelopment 环境不支持敏感变量标记
团队级策略可强制所有新建变量必须为 Sensitive

容量限制

Vercel 对环境变量有明确的容量限制:

  • 每个部署最多 64 KB 的环境变量总量
  • Edge Functions 和 Middleware(edge runtime)单个变量限制 5 KB

如果你的配置超过这些限制,需要重新评估是否有不必要的变量,或者考虑将配置拆分到外部服务。

系统自动注入变量

Vercel 会自动注入以下系统变量,无需手动配置:

变量名说明
VERCEL_URL当前部署的 URL
VERCEL_BRANCHGit 分支名
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_db

wrangler 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

两个平台的环境变量模型有本质区别,理解这些差异对于选择部署平台和迁移方案至关重要。

维度VercelCloudflare Workers
访问方式process.env.MY_VARenv.MY_VAR(fetch handler 参数)
敏感变量Dashboard 标记 Sensitivewrangler 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 流程图展示了从开发到生产的完整环境变量配置流程:

适读画布 · 130%
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:3000

Vercel 生产环境使用 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)
  }
}

关键差异在于:

  1. 访问方式process.env.KEYenv.KEY
  2. 类型定义:需要手动定义 Env 接口
  3. 密钥设置:Vercel Dashboard → wrangler secret put
  4. 本地开发.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)
  • 迁移或平台变更时,已确认环境变量的访问方式差异

参考资料

  1. The Twelve-Factor App — Config — 环境变量设计的理论基础
  2. Vercel Environment Variables — Vercel 官方文档
  3. Vercel Sensitive Environment Variables — Vercel 敏感变量配置
  4. Cloudflare Workers Environment Variables — Cloudflare 官方文档
  5. OWASP Secrets Management Cheat Sheet — OWASP 密钥管理指南
  6. Environment Variables: How to Use Them and 4 Critical Best Practices — Configu 最佳实践
  7. Best Practices for Environment Variables Secrets Management — GitGuardian 安全管理指南
  8. Storing Secrets in Env Vars Considered Harmful — Arcjet 环境变量安全分析