Workers Cache
摘要
Cloudflare 推出 Workers Cache,这是一种位于 Workers 前面的分层缓存,允许在不调用 Worker 的情况下提供缓存响应,从而减少 CPU 时间并提高性能。
暂无内容
查看缓存全文
缓存时间: 2026/07/06 14:03
# 你的 Worker 现在可以在前面拥有自己的缓存
来源:https://blog.cloudflare.com/workers-cache/
2026-07-06 · 17 分钟阅读
今天,我们推出 **Workers Cache**:一个位于 Worker 前面的分层缓存,只需一行 Wrangler 配置和你已经熟悉的 `Cache-Control` 头部即可开启。启用 Workers Cache 后,每个可缓存的请求都会先命中 Cloudflare 的缓存。如果存在新鲜的缓存响应,Cloudflare 会直接返回——你的 Worker 不会运行,你也不需要为 CPU 时间付费。如果缓存未命中,你的 Worker 会运行,如果响应是可缓存的,Cloudflare 会将其存储以供后续请求使用。下一次来自全球任何地方的请求都可以直接从缓存中获取。
BLOG-3262 image1
整个过程就是一个配置块:
```
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2026-05-01",
"cache": {
"enabled": true
}
}
```
之后,你按照 HTTP 一直期望的方式控制缓存——通过在响应中设置头部:
```javascript
return new Response(body, {
headers: {
"Cache-Control": "public, max-age=300, stale-while-revalidate=3600",
"Cache-Tag": "products,product:123",
},
});
```
当内容发生变化时,你的 Worker 可以清除自己的缓存:
```javascript
await ctx.cache.purge({ tags: ["product:123"] });
```
整个 API 就是这些。无需配置域、无需设置规则引擎、无需额外预置缓存、也无需登录另一个产品。Worker 的代码就是配置界面,缓存会跟随 Worker 运行在任何地方——自定义域名、`workers.dev`、服务绑定之后、预览环境、Workers for Platforms 租户中。一个 Worker,一个缓存,一次配置。这就是所有需要做的。
在底层有很多内容:遍布整个网络的分层缓存、完全支持 `stale-while-revalidate`(这样陈旧的响应永远不会阻塞用户)、通过 `Vary` 进行内容协商、通过 `ctx.props` 实现多租户安全的缓存键、按标签或路径前缀进行程序化清除,以及——我们认为是最大的突破——缓存位于每个 Worker 入口之前,而不仅仅是公共入口,并且可以按入口控制哪些需要缓存、哪些不需要。最后一点意味着你可以将缓存直接组合到应用的结构中:一个入口链,缓存阶段可以放在任何需要的地方,由两边的代码来配置。
下面我们会详细介绍所有内容。
Workers Cache 今天对所有 Worker(任何计划)开放,只需在 Wrangler 中启用。这是我们一直希望 Worker 拥有的缓存 API。以下是我们为什么花了这么长时间、它带来了哪些新可能,以及下一步的计划。
## 为什么服务端渲染的应用需要前置缓存
当我们 2017 年推出 Workers 时,我们的定位是,你可以在 Cloudflare 网络上运行代码,在请求到达源站之前对其进行转换。Worker 位于缓存和源站之前:
BLOG-3262 image5
对于当时的目标场景,这是正确的模型。如果你想为每个请求添加一个头部、重写 URL、进行 A/B 测试、或在流量到达源站前进行过滤,将 Worker 放在缓存和源站之前,可以让你完全控制哪些内容被缓存、哪些不被缓存。客户用它构建了令人惊叹的东西。
但世界变了。Worker 不再是附加在源站上的东西,而是成为了源站本身。诸如 Astro、TanStack Start、Next.js、Remix 和 SvelteKit 等框架都提供了 Cloudflare 适配器,将你的应用构建为 Worker。背后不再有源站。Worker *就是*服务器。
当 Worker 是源站时,原有的架构就没有东西可以缓存了。每个请求都会运行你的代码,即使响应与一秒前返回的响应逐字节相同。Workers 运行时足够快,可以处理每秒数千万的请求而毫不费力——但“足够快到渲染每个请求”仍然会在每次页面加载时带来延迟,并且每次调用都会消耗 CPU 时间。而对于服务端渲染的应用,每次页面加载本质上都是一次渲染。
Workers Cache 颠覆了这种架构。现在 Cloudflare 的缓存位于 Worker 之前:
BLOG-3262 image9
缓存命中时,你的 Worker 根本不会运行。Cloudflare 返回缓存的响应,你的 CPU 账单保持为零。缓存未命中时,你的 Worker 运行一次,填充缓存,下一次来自任何地方的请求都可以从缓存中获取,无需调用你的代码。
这就是服务端渲染在 Workers 上一直缺少的东西。过去你不得不在两个不令人满意的选项中选择:
- **在构建时预渲染所有内容**(“静态站点生成”)。页面加载快,但每次更改都需要完全重建和重新部署。对于一个几千页的文档站点,需要 5–10 分钟。对于大型电商站点,情况更糟——而且每次改动都要运行构建。
- **每个请求都渲染页面。** 内容始终最新,但每次页面加载都要付出渲染成本,每个访问者都要承受延迟。
Workers Cache 提供了第三种选择:按需服务端渲染,缓存渲染后的响应,并在你选择的 TTL 后刷新。对新页面的第一个请求仍然会渲染。随后的每个请求,在缓存过期之前,都像静态页面一样直接提供。当缓存过期时,下一个请求会触发重新渲染——而有了 `stale-while-revalidate`,甚至连那个请求也不需要等待。你获得了静态站点的速度,却没有构建时间;拥有了服务端渲染的新鲜度,却没有成本。无需任何框架特定的机制,比如增量静态再生成。只是 HTTP 缓存,按照设计方式工作,位于设计为源站的代码之前。
## `stale-while-revalidate` 是关键,让一切感觉瞬间完成
`stale-while-revalidate` 指令告诉 Cloudflare,当缓存的响应过期时,允许立即返回陈旧的副本,同时在后台刷新响应。Cloudflare 今年早些时候完全支持了 `stale-while-revalidate`,正是这个指令将“我们会缓存你的 Worker”变成了“你的 Worker 站点感觉像静态的”。
如果没有它,缓存条目过期后的第一个请求必须等待 Worker 重新渲染页面。用户会感受到那个延迟。有了它,过期后的第一个请求会立即获得陈旧的页面(带有 `Cf-Cache-Status: UPDATING` 头部),同时 Worker 在后台运行以重新填充缓存。每个用户,包括触发刷新的那个,都会得到缓存速度的响应。
BLOG-3262 image3
在实践中,代码是这样的:
```javascript
export default {
async fetch(request) {
const html = await renderPage(request);
return new Response(html, {
headers: {
"Content-Type": "text/html; charset=utf-8",
// 5 分钟内视为新鲜;在一个小时内提供陈旧内容,同时后台刷新。
"Cache-Control": "public, max-age=300, stale-while-revalidate=3600",
},
});
},
};
```
理解这个模型的要点:
- **新鲜窗口** (`max-age`):Cloudflare 提供缓存的响应。你的 Worker 不运行。
- **陈旧窗口** (`stale-while-revalidate`):Cloudflare 提供缓存的响应。你的 Worker 在后台运行以刷新。没有用户需要等待。
- **两个窗口之外**:Cloudflare 运行你的 Worker 生成新鲜响应,用户需要等待那一次渲染。
由你选择窗口大小。对于一个每几分钟更新一次的产品目录,`max-age=300`, `stale-while-revalidate=3600` 意味着访问者基本不需要等待,而你的 Worker 仍然足够频繁地运行以保持内容新鲜。对于一个几乎不变化的博客归档,`max-age=86400`, `stale-while-revalidate=2592000` 意味着你的 Worker 每页每天只运行一次。对新页面的第一个请求是唯一一次付出完整渲染成本的。之后,该页面在访问者看来就像静态输出,而你的 Worker 仍然掌控着页面如何生成。
## 一个 URL,多种表示:`Vary` 在这里有效
真实的应用很少对所有客户端返回相同的字节。同样的产品页面可能对浏览器返回 HTML,对 API 客户端返回 JSON。同样的图片可能对支持的客户端返回 WebP,对不支持的返回 JPEG。同样的主页可能根据用户的语言返回英文、法文或日文。
在没有缓存的情况下做到这一点很容易——你的 Worker 只需读取请求头部并返回正确的内容。但在有了缓存的情况下,通常会变得棘手。大多数缓存给你两个糟糕的选择:对具有多种表示的 URL 什么都不缓存,或者只缓存一种表示并提供给所有人。
Workers Cache 支持标准的 HTTP `Vary` 头部,这是解决这个问题的正确方法。当你的 Worker 返回带有 `Vary: Accept-Encoding`(或 `Accept`、`Accept-Language` 或其他请求头部)的响应时,Cloudflare 会为这些头部的每种不同组合存储一个单独的缓存变体——并且只返回那些存储值匹配传入请求的变体。
```javascript
export default {
async fetch(request) {
const accept = request.headers.get("Accept") ?? "";
const wantsWebp = accept.includes("image/webp");
const body = wantsWebp ? await fetchWebpImage() : await fetchJpegImage();
return new Response(body, {
headers: {
"Content-Type": wantsWebp ? "image/webp" : "image/jpeg",
"Cache-Control": "public, max-age=3600",
Vary: "Accept",
},
});
},
};
```
一个 URL,两个缓存的变体。发送 `Accept: image/webp,*/*` 的浏览器得到 WebP。发送 `Accept: image/jpeg` 的浏览器得到 JPEG。两者都来自缓存。你的 Worker 只在第一次请求每个变体时写入一次,之后对两者都不再运行。
这是内容协商的成熟 HTTP 标准,Workers Cache 按照 RFC 9110 和 RFC 9111 的描述实现。没有允许列表限制你可以对哪些头部使用 `Vary`。你列出任何需要的头部,Cloudflare 会根据精确的值来区分变体。文档中详细介绍了边缘情况——如何通过网关 Worker 规范化头部来控制变体扩散、为什么清除操作会一起失效 URL 的所有变体,以及唯一会完全禁用缓存的情况(`Vary: *`)。
## 这是你的 Worker 的缓存,而不是你的域的缓存
在我们讨论所有这些带来哪些新可能之前,有一个概念上的转变值得一提。Cloudflare 早已有了缓存。它在域级别配置:缓存规则、页面规则、缓存文件扩展名列表、缓存保留、分层缓存拓扑、自定义缓存键。所有这些都按域设置,以往 Worker 要么适应域的配置,要么绕开它。
Workers Cache 不同。它是**你的 Worker 的缓存**——它属于 Worker,而不是一个域。这带来了一系列重要的后果:
- **无需管理域配置。** 缓存规则、缓存级别设置、文件扩展名列表、页面规则——这些都不适用于 Workers Cache。Worker 的 `Cache-Control` 头部就是配置。
- **缓存跟随 Worker,而不是主机名。** 一个绑定到 `api.example.com`、`api.example.net` 并通过服务绑定调用的 Worker,在三个入口之间共享一个缓存。对 `/users/42` 的请求,无论从哪个入口进来,都会命中同一个缓存条目。
- **缓存可以在 `workers.dev` 上工作。** 它可以在预览 URL 中工作(每个预览有自己的缓存,因此测试更改不会污染生产环境)。它可以在 Workers for Platforms 中工作(每个用户 Worker 有自己的缓存,与调度器和其他租户隔离)。所有这些在以往都是缓存中的二等公民,现在不再是了。
- **清除操作限定在 Worker 的入口范围内。** 当你调用 `ctx.cache.purge({ purgeEverything: true })` 时,你只清除你的 Worker 入口的缓存。不会影响域的其他内容,也不会因为一个 Worker 的部署而导致另一个 Worker 的数据失效。
关于缓存的配置,你都在代码中完成:哪些路径需要更长的 TTL(根据路径分支并设置不同的 `max-age`)、哪些请求跳过缓存(返回 `Cache-Control: private`)、缓存键如何构造(控制进入 `ctx.props` 的内容、在网关 Worker 中规范化 URL 再分发)。你已有的 Worker 本身就是配置界面。完整文档在 Workers Cache: 你的 Worker 的缓存中有深入介绍。
## 每个 Worker 默认两层缓存,无需配置
Workers Cache **默认启用区域性分层**。有两个层级:
- **下层**位于离用户最近的 Cloudflare 数据中心。每个处理你 Worker 流量的数据中心都有自己的下层缓存。
- **上层**在整个网络范围内聚合填充。上层数量较少,每个下层在未命中时会询问上层。
请求首先命中下层。如果命中,直接返回响应,结束。如果未命中,下层询问上层。如果上层命中,响应返回并同时存储在下层。只有两层都未命中时,你的 Worker 才会实际运行——运行后的响应会同时存储到两层。
BLOG-3262 image2
这之所以重要,是因为**全球第一个请求**就会填充上层。随后的每个请求,无论来自哪个数据中心,都可以从上层获取,而无需你的 Worker 运行——即使该数据中心的本地层从未见过这个请求。缓存命中率远高于单一扁平缓存层,这正是当你 Worker 作为源站时希望看到的。
这与当前域的 Tiered Cache 所使用的是相同的拓扑结构,只不过你不需要配置它。没有“为我的 Worker 开启分层缓存”的对话框。每个启用缓存的 Worker 都会免费获得分层缓存。如果你的 Worker 使用了 Smart Placement,缓存可以与其干净地组合:首先查询分层缓存,只有两层都未命中时,Smart Placement 才会将执行路由到接近你源站的位置。关于它们如何交互,我们还有更多要说……
相似文章
@geekbb: 这个好,一个开源内存层,让 Claude、ChatGPT、Cursor 和 Codex 共享一个大脑。 主要是它部署在 Cloudflare Workers 上,把记忆放在你自己 Cloudflare 账号里,对外走 MCP,数据落在用户…
Second Brain is an open-source memory layer that lets Claude, ChatGPT, Cursor, and Codex share persistent, semantic searchable memory, deployed on Cloudflare Workers for user-controlled storage.
你可能不需要 Service Worker
文章质疑了许多 Web 应用中对 Service Worker 的必要性,认为简单的 HTTP 缓存通常就足够了,而 Service Worker 会带来复杂性和缓存过期的风险。
面向AI代理的临时Cloudflare账户
Cloudflare宣布推出临时账户,允许开发者在无需注册的情况下将Workers部署到临时项目中,账户有效期为60分钟,并可选择申请永久保留,主要面向AI代理,但具有广泛用途。
@realchendahuang: 我感觉大家对 Cloudflare 的开发程度依然不足 1%。 它现在出了太多的功能。 对象存储用 R2。 后端 API 用 worker。 AI 网关用 AI Gateway。 重计算的,用 Container。 缓存用 KV。 数据库…
本推文介绍了Cloudflare提供的多种开发功能,包括对象存储R2、后端API Worker、AI网关AI Gateway、容器、缓存KV、数据库D1和PostgreSQL连接HyperDrive等,强调其价格便宜、功能丰富且免费额度充足。
Cloudflare 将裁员约 20%
Cloudflare 宣布裁员约 20%,作为其面向未来发展战略的一部分。