环境变量读取
要点
- 密钥和配置不能写死在代码里,这是工程基本纪律
- Cloudflare Workers 没有
process.env,环境变量通过c.env读取 - Workers 的环境变量分两类:Vars(非敏感)和 Secrets(敏感)
- 本地开发使用
.dev.vars文件管理变量,wrangler 自动加载 - 不同环境(本地、预览、生产)各有各的配置策略
- TypeScript 需要手动为
c.env定义类型接口
1. 为什么不能把密钥硬编码
先看一个常见的错误做法:
// src/index.ts — 错误示范
app.get('/api/weather', async (c) => {
const apiKey = 'sk-proj-abc123def456'
const res = await fetch(`https://api.weather.com/v1?key=${apiKey}&city=beijing`)
const data = await res.json()
return c.json(data)
})API Key 直接写在代码里,跑起来确实能用,但会带来两个实际问题。
安全风险:代码一旦泄露(推到公开仓库、同事转发、截屏分享),密钥就一起泄露了。攻击者拿到你的 Key 可以直接消费你的 API 额度,甚至用你的身份做违规操作。密钥泄露后的处理成本远高于写进 .env 的成本。
环境差异:本地开发、预览环境、生产环境用的配置往往不同。本地调测试 API,线上调正式 API;本地连开发数据库,线上连生产数据库。如果配置写死在代码里,切环境就得改代码、重新部署。
正确做法是把敏感信息放在环境变量里,代码只读取、不存储。
2. Cloudflare Workers 的环境变量机制
如果你之前写过 Node.js,可能会习惯这样读环境变量:
// 这在 Node.js 里能用,在 Workers 里不行
const apiKey = process.env.MY_API_KEYCloudflare Workers 运行在 V8 隔离沙箱里,没有 Node.js 的 process 对象,也没有 process.env。直接访问会得到 undefined。
Workers 的环境变量通过 env 对象注入。在 Workers 的原生 fetch handler 里,env 是第二个参数:
// 原生 Workers fetch handler
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const apiKey = env.MY_API_KEY
return new Response(`API Key: ${apiKey}`)
}
}在 Hono 里,env 被挂在 Context 对象上,通过 c.env 访问:
// Hono 写法
app.get('/', (c) => {
const apiKey = c.env.MY_API_KEY
return c.text(`API Key: ${apiKey}`)
})记住一个核心区别:Workers 里读环境变量用 c.env.KEY,不是 process.env.KEY。
Cloudflare Workers 的环境变量分两种类型:
| 类型 | 用途 | 存储方式 | 适合放什么 |
|---|---|---|---|
| Vars | 非敏感配置 | 写在 wrangler.jsonc 里,Dashboard 可见 | 环境标识、API 端点、功能开关 |
| Secrets | 敏感凭证 | 加密存储,Dashboard 中只能看到是否已设置 | API Key、数据库密码、JWT 签名密钥 |
3. 在 wrangler.jsonc 中配置 Vars
Vars 用于不需要保密的配置项。在 wrangler.jsonc 的 vars 字段里定义:
// wrangler.jsonc
{
"name": "my-api",
"main": "src/index.ts",
"compatibility_date": "2026-04-15",
"vars": {
"ENVIRONMENT": "production",
"API_BASE_URL": "https://api.example.com"
}
}代码里直接读取:
// src/index.ts
app.get('/api/config', (c) => {
return c.json({
environment: c.env.ENVIRONMENT,
apiBaseUrl: c.env.API_BASE_URL,
})
})部署后,wrangler 会把 vars 里的值注入到 Worker 运行时,通过 c.env 就能读到。
Vars 适合放这些内容:
- 当前环境标识(
development/staging/production) - 外部 API 的基础 URL
- 功能开关(
ENABLE_NEW_UI: true) - 不需要保密的业务常量
不要把 API Key、密码、Token 放在 vars 里——它们会明文出现在代码和配置文件中,也会被推送到 Git 仓库。
4. 使用 Wrangler Secrets 管理敏感信息
API Key、数据库连接串、JWT 密钥这类敏感信息,需要用 Wrangler Secrets 来管理。
设置一个 Secret:
// terminal
npx wrangler secret put MY_API_KEY终端会提示你输入值,输入后 Secret 会被加密上传到 Cloudflare,不会出现在任何配置文件里。
代码里读取方式和 Vars 完全一样:
// src/index.ts
app.get('/api/data', async (c) => {
const apiKey = c.env.MY_API_KEY
const res = await fetch('https://api.example.com/data', {
headers: {
'Authorization': `Bearer ${apiKey}`,
},
})
const data = await res.json()
return c.json(data)
})Secrets 的管理方式:
- 命令行:
npx wrangler secret put KEY_NAME设置,npx wrangler secret list查看已有 Secret 列表(只能看到名称,看不到值) - Dashboard:在 Cloudflare Dashboard → Workers → 你的 Worker → Settings → Variables 中管理,可以添加、修改、删除 Secret,但同样看不到 Secret 的具体值
这个设计保证了即使有 Dashboard 查看权限的人,也无法直接看到密钥内容。
5. 本地开发时的环境变量
本地开发时不方便每次都用 wrangler secret put。wrangler 提供了 .dev.vars 文件来解决这个问题。
在项目根目录创建 .dev.vars 文件,格式类似 .env:
# .dev.vars
MY_API_KEY=sk-test-local-key-12345
DB_URL=postgres://localhost:5432/mydb
JWT_SECRET=local-dev-secret-do-not-use-in-production运行 npx wrangler dev 时,wrangler 会自动加载这个文件,把里面的变量注入到 c.env。代码里的读取方式和线上完全一致,不需要任何额外处理。
.dev.vars 必须加到 .gitignore:
# .gitignore
.dev.vars这个文件只存在于本地,不会提交到仓库。每个开发者在自己机器上维护一份。可以提供一个 .dev.vars.example 作为模板,列出需要的变量名和示例值(不含真实密钥),方便新同事配置本地环境:
# .dev.vars.example
MY_API_KEY=your-api-key-here
DB_URL=postgres://localhost:5432/mydb
JWT_SECRET=your-jwt-secret-here6. TypeScript 类型定义
c.env 默认类型是 unknown 或 any,直接访问属性不会有类型提示和编译时检查。需要手动定义一个接口,把环境变量的类型告诉 TypeScript:
// src/env.ts
export interface Env {
// Vars — 在 wrangler.jsonc 的 vars 中定义
ENVIRONMENT: string
API_BASE_URL: string
// Secrets — 通过 wrangler secret put 设置
MY_API_KEY: string
DB_URL: string
JWT_SECRET: string
}然后在创建 Hono 实例时传入泛型参数:
// src/index.ts
import { Hono } from 'hono'
import type { Env } from './env'
const app = new Hono<{ Bindings: Env }>()
app.get('/api/data', async (c) => {
// c.env.MY_API_KEY 现在有类型提示,是 string
const apiKey = c.env.MY_API_KEY
const baseUrl = c.env.API_BASE_URL
// ...
})
export default app{ Bindings: Env } 是 Hono 的泛型参数,告诉框架「这个应用的 c.env 符合 Env 接口」。配置完成后,编辑器会在你写 c.env. 时自动列出所有可用的变量名,拼错变量名也会在编译时报错。
7. 不同环境的配置策略
一个项目通常有多个部署环境,每个环境的配置值不同:
本地开发
- 非敏感变量:写在
wrangler.jsonc的vars里 - 敏感变量:写在
.dev.vars里 - 启动方式:
npx wrangler dev,wrangler 自动合并两者注入到c.env
预览环境(Preview)
在 wrangler.jsonc 里用 env.preview 覆盖配置:
// wrangler.jsonc
{
"name": "my-api",
"main": "src/index.ts",
"compatibility_date": "2026-04-15",
"vars": {
"ENVIRONMENT": "production"
},
"env": {
"preview": {
"name": "my-api-preview",
"vars": {
"ENVIRONMENT": "preview"
}
}
}
}预览环境的 Secrets 需要单独设置:npx wrangler secret put MY_API_KEY --env preview。
生产环境
- 非敏感变量:
wrangler.jsonc顶层vars - 敏感变量:
npx wrangler secret put MY_API_KEY(不带--env即默认生产环境) - 部署:
npx wrangler deploy
总结成一张表:
| 环境 | 非敏感变量来源 | 敏感变量来源 |
|---|---|---|
| 本地 | wrangler.jsonc 的 vars | .dev.vars 文件 |
| 预览 | wrangler.jsonc 的 env.preview.vars | wrangler secret put --env preview |
| 生产 | wrangler.jsonc 的 vars | wrangler secret put |
代码层面不需要做任何环境判断,c.env.ENVIRONMENT 的值在不同环境自动不同,同一个路由处理函数在所有环境下行为一致。
8. 完整示例
一个调用外部天气 API 的路由,把 API Key 存在环境变量里:
// src/index.ts
import { Hono } from 'hono'
// 定义环境变量类型
interface Env {
WEATHER_API_KEY: string
WEATHER_API_URL: string
ENVIRONMENT: string
}
const app = new Hono<{ Bindings: Env }>()
// 返回当前环境信息
app.get('/', (c) => {
return c.json({
service: 'Weather API Gateway',
environment: c.env.ENVIRONMENT,
})
})
// 调用外部天气 API
app.get('/api/weather/:city', async (c) => {
const city = c.req.param('city')
const apiKey = c.env.WEATHER_API_KEY
const apiUrl = c.env.WEATHER_API_URL
if (!apiKey) {
return c.json({ error: 'API Key not configured' }, 500)
}
try {
const res = await fetch(
`${apiUrl}/v1/current?key=${apiKey}&q=${encodeURIComponent(city)}`
)
if (!res.ok) {
return c.json({ error: 'Weather API error', status: res.status }, 502)
}
const data = await res.json()
return c.json({ city, temperature: data.current?.temp_c, data })
} catch (error) {
return c.json({ error: 'Failed to fetch weather data' }, 500)
}
})
export default app对应的配置文件:
// wrangler.jsonc
{
"name": "weather-api",
"main": "src/index.ts",
"compatibility_date": "2026-04-15",
"vars": {
"ENVIRONMENT": "production",
"WEATHER_API_URL": "https://api.weatherapi.com"
}
}# .dev.vars — 本地开发用,加入 .gitignore
WEATHER_API_KEY=your-local-test-key部署前设置生产环境的 Secret:
// terminal
npx wrangler secret put WEATHER_API_KEY
# 按提示输入真实的 API Key这个例子覆盖了几个关键实践:
- 敏感信息(
WEATHER_API_KEY)通过环境变量读取,不硬编码 - 非敏感常量(
WEATHER_API_URL)放在wrangler.jsonc的vars里 - 用
{ Bindings: Env }为c.env提供类型定义 - 读取环境变量后做了空值检查,防止配置遗漏导致运行时异常
延伸阅读
总结
这篇讲了 Hono + Cloudflare Workers 环境下的配置管理。核心就几件事:
Workers 没有 process.env,读环境变量用 c.env。环境变量分 Vars(非敏感,写在配置文件)和 Secrets(敏感,加密存储)。本地开发用 .dev.vars 文件,记得加到 .gitignore。TypeScript 需要手动定义 Env 接口,让 c.env 有类型提示。不同环境各自维护配置,代码层面不做环境判断。
密钥永远不要写进代码——这是比任何框架技巧都重要的工程习惯。