环境变量读取

要点

  • 密钥和配置不能写死在代码里,这是工程基本纪律
  • 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_KEY

Cloudflare 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.jsoncvars 字段里定义:

// 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-here

6. TypeScript 类型定义

c.env 默认类型是 unknownany,直接访问属性不会有类型提示和编译时检查。需要手动定义一个接口,把环境变量的类型告诉 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

&#123; Bindings: Env &#125; 是 Hono 的泛型参数,告诉框架「这个应用的 c.env 符合 Env 接口」。配置完成后,编辑器会在你写 c.env. 时自动列出所有可用的变量名,拼错变量名也会在编译时报错。

7. 不同环境的配置策略

一个项目通常有多个部署环境,每个环境的配置值不同:

本地开发

  • 非敏感变量:写在 wrangler.jsoncvars
  • 敏感变量:写在 .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.jsoncvars.dev.vars 文件
预览wrangler.jsoncenv.preview.varswrangler secret put --env preview
生产wrangler.jsoncvarswrangler 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.jsoncvars
  • &#123; Bindings: Env &#125;c.env 提供类型定义
  • 读取环境变量后做了空值检查,防止配置遗漏导致运行时异常

延伸阅读

总结

这篇讲了 Hono + Cloudflare Workers 环境下的配置管理。核心就几件事:

Workers 没有 process.env,读环境变量用 c.env。环境变量分 Vars(非敏感,写在配置文件)和 Secrets(敏感,加密存储)。本地开发用 .dev.vars 文件,记得加到 .gitignore。TypeScript 需要手动定义 Env 接口,让 c.env 有类型提示。不同环境各自维护配置,代码层面不做环境判断。

密钥永远不要写进代码——这是比任何框架技巧都重要的工程习惯。