Cloudflare 新推出的 Workers Cache,把一个按区域分层的缓存层直接放在 Worker entrypoint 前面。关键变化不是“又多了一个缓存 API”,而是请求在进入 Worker 代码之前就有机会命中缓存,而且配置方式回到开发者熟悉的 HTTP 头:Cache-Control、Vary、ETag 这类协议级信号。
这对 Worker 应用的影响很直接:热点响应可以少进运行时,边缘函数可以少做重复计算,后端 API 也能少挨打。更重要的是,它保留了 Worker 的组合能力,缓存策略可以和路由、鉴权、重写、上游请求一起设计。
缓存在 Worker 前面,意味着什么
传统 Worker 场景里,请求通常先进入 Worker,再由代码决定是否查 caches.default、是否访问源站、是否拼响应。Workers Cache 的位置更靠前:它坐在 Worker entrypoint 前面,因此一次命中可以直接跳过 Worker 执行路径。
这类设计适合三种常见负载:
- 可公开缓存的 API 响应,例如产品列表、配置 JSON、文档索引。
- 计算成本高但结果短时间稳定的页面片段,例如聚合页、排行榜、搜索建议。
- 由 Worker 代理出来的静态或半静态资源,例如图片变体、manifest、RSS。
边界也要说清楚:如果响应依赖用户身份、Cookie、Authorization 头,或者包含账户级私密数据,就不能粗暴设置为公共缓存。前置缓存越早命中,错误缓存的影响面也越大。
标准 HTTP 头重新变成控制面
来源摘要里最值得注意的一句是“configured via standard HTTP headers”。这意味着缓存策略不是藏在某个专用 SDK 调用里,而是体现在响应语义上。
可以这样理解常用头的职责:
Cache-Control: public, s-maxage=60:告诉共享缓存这个响应可以缓存 60 秒。stale-while-revalidate=300:允许缓存先返回旧内容,再异步更新,适合允许短暂陈旧的数据。Vary: Accept-Encoding或业务相关头:声明哪些请求头会影响响应版本。ETag:给再验证提供稳定标识,减少完整响应传输。
这让缓存策略更容易被 review。你不必在一堆分支逻辑里猜“这个响应到底会不会被缓存”,看响应头就能判断大半。
可以这样实践:给公开 API 加前置缓存语义
下面是一个可改造的 Worker 示例。它没有假设某个专有配置接口,只演示如何通过标准 HTTP 头表达“这个响应适合共享缓存”。把 https://api.example.com/products 换成你的真实上游地址即可。
export default {
async fetch(request) {
const url = new URL(request.url);
if (url.pathname !== "/products") {
return new Response("Not found", { status: 404 });
}
const upstream = await fetch("https://api.example.com/products", {
headers: {
"Accept": "application/json"
}
});
if (!upstream.ok) {
return new Response("Upstream error", { status: 502 });
}
const body = await upstream.text();
return new Response(body, {
status: 200,
headers: {
"Content-Type": "application/json; charset=utf-8",
"Cache-Control": "public, s-maxage=60, stale-while-revalidate=300",
"Vary": "Accept-Encoding"
}
});
}
};
本地项目可以用最小结构启动:
mkdir workers-cache-demo
cd workers-cache-demo
npm init -y
npm install --save-dev wrangler
cat > wrangler.toml <<'EOF'
name = "workers-cache-demo"
main = "src/index.js"
compatibility_date = "2025-01-01"
EOF
mkdir -p src
# 将上面的 Worker 代码保存到 src/index.js
npx wrangler dev
再用 curl 看响应头是否符合预期:
curl -i http://localhost:8787/products
你要重点检查这些行:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: public, s-maxage=60, stale-while-revalidate=300
Vary: Accept-Encoding
上线前还可以为不同路径设定不同策略。例如列表页短缓存,详情页稍长缓存,用户相关接口完全禁止公共缓存:
function cacheHeadersFor(pathname) {
if (pathname.startsWith("/account")) {
return {
"Cache-Control": "private, no-store"
};
}
if (pathname.startsWith("/products/")) {
return {
"Cache-Control": "public, s-maxage=300, stale-while-revalidate=600",
"Vary": "Accept-Encoding"
};
}
return {
"Cache-Control": "public, s-maxage=60, stale-while-revalidate=300",
"Vary": "Accept-Encoding"
};
}
组合能力的真正价值
“Infinitely composable”听起来像宣传词,但放在 Worker 体系里有实际含义。Worker 通常已经承担路由、A/B 实验、请求归一化、后端代理、响应改写等职责。前置缓存如果只支持一刀切规则,价值会被限制;如果它能跟 HTTP 头和 Worker 响应自然组合,工程团队就能把缓存策略写进应用边界。
一个实用做法是先做请求归一化,再让缓存看到稳定的响应语义。例如:
- 删除不影响响应的 query 参数,避免缓存碎片。
- 对会影响内容的参数保留顺序和格式,避免错误合并。
- 只对
GET和HEAD返回公共缓存头。 - 对带
Authorization或敏感 Cookie 的请求返回no-store。
可以这样实践一个防误缓存的判断函数:
function isPublicCacheCandidate(request) {
if (request.method !== "GET" && request.method !== "HEAD") {
return false;
}
const headers = request.headers;
if (headers.has("Authorization")) {
return false;
}
const cookie = headers.get("Cookie") || "";
if (cookie.includes("session=") || cookie.includes("auth=")) {
return false;
}
return true;
}
上线前的检查清单
采用 Workers Cache 时,不要从“缓存一切”开始。更稳的路径是挑一个公开、读多写少、错误成本可控的入口,先把响应头设计清楚。
上线前建议检查:
- 是否只缓存公开数据,用户态响应是否明确
private或no-store。 s-maxage是否符合业务更新频率,而不是拍脑袋给一个很长时间。- 是否需要
stale-while-revalidate来降低尾延迟。 Vary是否足够小,避免缓存版本爆炸。- 是否有观测手段确认命中率、源站流量和错误缓存。
Workers Cache 的价值在于把缓存从“代码里的一段逻辑”提升到 Worker 入口前的协议层能力。用得好,它能减少运行时执行和源站压力;用得粗糙,它也会把错误响应更快、更稳定地分发出去。把缓存头当作接口契约来设计,是采用它时最重要的工程纪律。