Cloudflare Workers 最佳实践
☁️ Cloudflare Workers 最佳实践
基于生产环境模式、Cloudflare 内部使用以及开发者社区常见问题总结的 Workers 最佳实践。
📋 配置
保持兼容性日期为最新
compatibility_date 控制你的 Worker 可以使用哪些运行时功能和错误修复。在新项目中将其设置为今天的日期可确保你获得最新的行为。在现有项目中定期更新它,无需更改代码即可获得新的 API 和修复。
// wrangler.jsonc
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2026-02-16",
"compatibility_flags": ["nodejs_compat"],
}
启用 nodejs_compat
nodejs_compat 兼容性标志让你的 Worker 可以访问 Node.js 内置模块,如 node:crypto、node:buffer、node:stream 等。许多库依赖这些模块,启用此标志可避免运行时出现神秘的导入错误。
使用 wrangler types 生成绑定类型
不要手写 Env 接口。运行 wrangler types 生成与你的实际 Wrangler 配置匹配的类型定义文件。
Env 由 wrangler types 生成,始终与你的配置匹配。不要手动定义 Env —— 它会与实际绑定产生偏差。
使用 wrangler secret 存储密钥
密钥(API 密钥、令牌、数据库凭证)绝不能出现在你的 Wrangler 配置或源代码中。
wrangler secret put API_KEY
# 对于本地开发,使用 .env(确保它在 .gitignore 中)
# API_KEY=sk-test-abc123
永远不要在 wrangler.jsonc 或 wrangler.toml 中放置密钥
正确设置自定义域名或路由
- 自定义域名:Worker 是源站,Cloudflare 自动创建 DNS 记录和 SSL 证书
- 路由:Worker 在现有源站服务器前面运行,需要代理 DNS 记录
🔄 请求和响应处理
流式传输请求和响应体
无论内存限制如何,流式传输大型请求和响应都是最佳实践。它可以减少峰值内存使用并改善首字节时间。
// 将整个响应体缓冲到内存中
const badHandler = {
async fetch(request, env) {
const response = await fetch("https://api.example.com/large-dataset");
const text = await response.text(); // ❌ 会崩溃
return new Response(text);
},
};
// 流式传输响应体而不缓冲
export default {
async fetch(request, env) {
const response = await fetch("https://api.example.com/large-dataset");
return new Response(response.body, response); // ✅ 流式传输
},
};
使用 waitUntil 在响应后执行工作
ctx.waitUntil() 允许你在响应发送到客户端后执行工作,例如分析、缓存写入、非关键日志记录。
解构 ctx 会丢失 this 绑定,抛出 "Illegal invocation" 错误
export default {
async fetch(request, env, ctx) {
const data = await processRequest(request);
ctx.waitUntil(logToAnalytics(env, data));
ctx.waitUntil(updateCache(env, data));
return Response.json(data);
},
};
🏗️ 架构
使用绑定访问 Cloudflare 服务
不要使用 REST API,直接使用绑定(R2、KV、D1、Queues、Workflows)。
// 直接使用绑定 —— 无需网络跳转,无需身份验证
export default {
async fetch(request, env) {
const object = await env.MY_BUCKET.get("my-file");
if (!object) {
return new Response("Not found", { status: 404 });
}
return new Response(object.body, {
headers: { "Content-Type": object.httpMetadata?.contentType }
});
},
};
使用 Queues 和 Workflows 处理后台工作
- 使用 Queues 当 你需要将生产者与消费者解耦,处理单步后台作业
- 使用 Workflows 当 后台工作有多个相互依赖的步骤,需要持久化执行
使用 Hyperdrive 连接外部数据库
从 Worker 连接到远程 PostgreSQL 或 MySQL 数据库时,始终使用 Hyperdrive。它维护区域连接池,消除 TCP 握手、TLS 协商成本。
使用 Durable Objects 处理 WebSockets
对于可靠、长期的 WebSocket 连接,使用带有休眠 API 的 Durable Objects。
📊 可观测性
启用 Workers Logs 和 Traces
在部署到生产环境之前启用日志和追踪。使用结构化 JSON 日志记录。
// 结构化 JSON —— 可搜索和可过滤
console.log(
JSON.stringify({
message: "incoming request",
method: request.method,
path: url.pathname,
})
);
// console.error 显示为 "error" 严重性
console.error(
JSON.stringify({
message: "request failed",
error: e.message,
})
);
非结构化字符串日志难以查询:console.log("Got a request to " + url.pathname)
💻 代码模式
不要在全局作用域中存储请求作用域状态
Workers 在请求之间重用 isolates。全局变量会导致跨请求数据泄漏。
通过函数参数传递请求范围的数据
始终 await 或 waitUntil 你的 Promises
浮动 promise 会导致静默 bug:丢失结果、吞下错误和未完成的工作。
🔒 安全
使用 Web Crypto 生成安全令牌
Math.random() 是可预测的,不适合安全用途
// 加密安全的随机 UUID
const sessionId = crypto.randomUUID();
// 加密安全的随机字节用于令牌
const tokenBytes = new Uint8Array(32);
crypto.getRandomValues(tokenBytes);
不要使用 passThroughOnException 作为错误处理
使用显式 try/catch 块与结构化错误响应代替。
🧪 开发和测试
使用 @cloudflare/vitest-pool-workers 进行测试
在 Workers 运行时内运行你的测试,获得真实的绑定访问。
npx eslint --rule '{"@typescript-eslint/no-floating-promises": "error"}' src/
评论