The Cloudflare Blog

Your Worker can now have its own cache in front of it

8.5内容质量
Your Worker can now have its own cache in front of it

TL;DR · AI 摘要

Cloudflare推出Workers Cache,允许在Worker前配置缓存,通过简单配置和Cache-Control头实现高效缓存管理,提升性能并降低CPU成本。

核心要点

  • Workers Cache通过单行Wrangler配置启用,无需额外设置,简化缓存管理。
  • 使用Cache-Control头和Cache-Tag实现细粒度缓存控制,支持按标签清除缓存。
  • 分层缓存和stale-while-revalidate支持提升缓存命中率和用户体验。

结构提纲

按章节快速跳转。

  1. 介绍Workers Cache的发布及其主要功能。

  2. 通过Wrangler配置和Cache-Control头实现缓存管理。

  3. 支持分层缓存、stale-while-revalidate和多租户安全缓存键。

  4. 代码示例展示如何配置和使用缓存清除功能。

  5. 讨论该功能带来的可能性和后续发展。

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • Workers Cache
    • 核心机制
      • Wrangler配置
      • Cache-Control头
    • 高级功能
      • 分层缓存
      • stale-while-revalidate
    • 应用场景
      • 前端性能优化
      • 成本控制

金句 / Highlights

值得收藏与分享的关键句。

#Cloudflare#Workers Cache#前端#缓存技术
打开原文

现在您的 Worker 可以拥有自己的缓存层

2026-07-06

  • Dan Lapid
  • Connor Harwood

17 分钟阅读

今天,我们推出 Workers Cache:一种位于您的 Worker 前端的分层缓存,通过一行 Wrangler 配置和您已经熟悉的 Cache-Control 头即可完成设置。

启用 Workers Cache 后,所有可缓存的请求会首先命中 Cloudflare 的缓存。如果存在新鲜的缓存响应,Cloudflare 会直接返回它——您的 Worker 不会运行,也不会产生 CPU 时间费用。在缓存未命中时,您的 Worker 会运行,如果响应可缓存,Cloudflare 会将其存储以供下次请求使用。来自地球任何地方的下一次请求都可以直接从缓存中获取。

整个配置只需一个代码块:

code
{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2026-05-01",
  "cache": {
    "enabled": true
  }
}

之后,您可以按照 HTTP 一直希望的方式控制缓存——通过在响应中设置头字段:

code
return new Response(body, {
  headers: {
    "Cache-Control": "public, max-age=300, stale-while-revalidate=3600",
    "Cache-Tag": "products,product:123",
  },
});

当内容发生变化时,您的 Worker 可以通过以下方式清除自己的缓存:

code
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 启用。

这就是我们一直希望 Workers 拥有的缓存 API。下面我们将解释为什么这项功能耗时这么久才推出,它使哪些新可能性成为现实,以及接下来将推出哪些功能。

为什么服务端渲染应用需要前端缓存

2017 年我们推出 Workers 时,宣传点是您可以在 Cloudflare 网络上运行代码,以在请求到达源站之前对其进行转换。Worker 位于缓存和源站的前端:

这种模型对于当时我们针对的使用场景是正确的。如果您想为每个请求添加头字段、重写 URL、进行 A/B 测试或在请求到达源站前过滤流量,将 Worker 放在缓存和源站前端可以为您提供对缓存内容的完全控制。客户利用这一功能构建了令人惊叹的应用。

但世界已经改变。工作者(Worker)不再只是附加在源站上的组件,而是成为了源站本身。Astro、TanStack Start、Next.js、Remix 和 SvelteKit 等框架都提供了 Cloudflare 适配器,可以将你的应用构建为 Worker。它们背后没有源站,Worker 就是服务器。

当 Worker 成为源站时,原始架构就无法进行缓存。每个请求都会运行你的代码,即使响应内容与几秒前返回的完全一致。Workers 运行时足够快速,可以轻松处理每秒数千万次请求,但“足够快”仍然意味着每次页面加载都会产生延迟,每次函数调用都会消耗 CPU 时间。而在服务器渲染的应用中,每次页面加载本质上都是一次渲染。

Workers Cache 改变了架构。Cloudflare 的缓存现在位于 Worker 前端:

  • 缓存命中时,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 在后台运行以刷新缓存。每个用户(包括触发刷新的用户)都能获得缓存加速的响应。

实际效果如下:

code
export default {
  async fetch(request) {
    const html = await renderPage(request);
    return new Response(html, {
      headers: {
        "Content-Type": "text/html; charset=utf-8",
        // 保持5分钟新鲜度;在1小时内提供过期内容
        // 同时在后台刷新缓存
        "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的客户端的WebP格式,不支持的客户端的JPEG格式。首页可能根据用户语言返回英文、法文或日文版本。

在没有缓存的情况下实现这一点很简单——你的Worker只需读取请求头并返回对应内容。但使用缓存时通常会变得复杂。大多数缓存只提供两个糟糕的选项:对具有多种表现形式的URL不进行任何缓存,或仅缓存一种表现形式并提供给所有用户。

Workers Cache支持标准的HTTP Vary头,这是正确解决该问题的方式。当你的Worker返回包含Vary: Accept-Encoding(或Accept、Accept-Language,或其他任何请求头)的响应时,Cloudflare会为这些头字段每个不同的组合值存储独立的缓存变体——并且只返回与传入请求匹配的变体。

code
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",
        // 根据不同的Accept头值缓存独立变体
        Vary: "Accept",
      },
    });
  },
};

一个URL,两个缓存变体。发送Accept: image/webp,*/*的浏览器获取WebP格式。发送Accept: image/jpeg的浏览器获取JPEG格式。两者都来自缓存。你的Worker在首次请求每个URL时生成两种变体,之后对这两种变体都不再运行。

这是内容协商的成熟 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.comapi.example.net 并通过服务绑定调用的 Worker,其三个域名共享同一个缓存。无论请求路径如何,/users/42 的缓存条目始终一致。
  • 缓存支持 workers.dev 环境,在预览 URL(每个预览拥有独立缓存,避免测试影响生产)、平台 Worker(每个用户 Worker 拥有独立缓存,与调度器和其他租户隔离)中均能正常工作。这些场景过去一直是缓存的二等公民,现在已完全支持。
  • 清除操作作用域限定在 Worker 入口点。调用 ctx.cache.purge({ purgeEverything: true }) 时,只会清除当前 Worker 入口点的缓存。不会误伤区域的其他内容,也不会因某个 Worker 的部署影响其他 Worker 的数据。

关于缓存的配置,你需通过代码实现:哪些路径需要更长的 TTL(根据路径分支设置不同的 max-age)、哪些请求绕过缓存(返回 Cache-Control: private)、如何构建缓存键(控制 ctx.props 内容,在网关 Worker 中规范 URL 后再分发)。你已编写的 Worker 本身即为完整的配置界面。

Workers Cache 的完整文档在《Workers Cache:你的 Worker 的缓存》中深入探讨了这些特性。

每个 Worker 两层缓存,无需配置

Workers Cache 默认采用区域分层架构,包含两个层级:

  • 底层缓存:位于用户最近的 Cloudflare 数据中心。每个接收你 Worker 流量的数据中心都拥有独立的底层缓存。
  • 上层缓存:在整个网络中聚合填充。此类缓存数量更少,每个底层缓存在未命中时都会查询上层缓存。

请求首先命中底层缓存。若命中,直接返回响应。若未命中,底层缓存会查询上层缓存。若上层缓存命中,响应会返回并同步存储到底层缓存。只有当上下两层均未命中时,Worker 才会实际运行——且运行结果会同时存储到两个层级。

这之所以重要,是因为世界上任何地方的首个请求会填充上层缓存。此后所有请求(无论来自哪个数据中心)都可以直接从上层缓存获取响应,无需运行您的 Worker —— 即使该数据中心的下层缓存从未处理过该请求。与单层扁平缓存相比,缓存命中率会显著提升,这正是您希望看到的效果(当 Worker 作为源站时)。

这与当前为区域提供的分层缓存架构相同,区别在于您无需手动配置。没有“为我的 Worker 启用分层缓存”的对话框。所有启用了缓存的 Worker 都会自动获得分层缓存能力。

如果您的 Worker 使用了 Smart Placement,缓存可以与之无缝协作:系统会优先检查各层级缓存,仅在所有层级都未命中时,Smart Placement 才会将执行路由到靠近源站的位置。关于这些层级如何交互的更多细节(包括我们计划优化的一些粗糙点),请参阅文档。

在用户和数据附近运行您的应用

网络性能领域一直存在一个尚未完全解决的矛盾:您希望代码尽可能靠近用户(因为用户与服务器之间的往返延迟是关键路径),同时又希望代码尽可能靠近数据(因为每次数据库查询同样存在往返延迟)。选择其一,另一方就会变慢。

我们多年来一直在追求两者的平衡。我们的网络可将全球约 95% 的互联网用户延迟控制在 50 毫秒以内。Smart Placement 和 Placement Hints 让您无需考虑云区域即可保持代码靠近数据。但直到现在,这两者尚未完全协同工作。您只能选择“靠近用户”或“靠近数据”,如果希望应用的两个部分同时处于正确位置,就必须是 Cloudflare 专家。我们知道还可以做得更好。

Workers Cache 就是解决这一问题的关键。由于缓存属于 Worker(而非区域),且 Worker 之间的服务绑定和 ctx.exports 调用会经过缓存,您可以将应用构建为 Worker 链 —— 每个 Worker 都运行在最适合的位置,缓存则作为它们之间的连接点。

架构如下:

  • Worker A 运行在靠近用户的位置。它处理所有请求中成本低且对延迟敏感的部分:身份验证、限速、路由、头部规范化、渲染不依赖数据的 HTML 页面外壳。
  • Worker B 通过 Smart Placement 或显式 Placement Hint 运行在靠近数据的位置。它执行繁重任务:服务器端渲染需要获取数据的页面、读取产品目录、生成搜索结果、聚合 API、执行高成本转换。
  • Workers Cache 位于 Worker B 前端。当 Worker A 通过服务绑定调用 Worker B 时,Cloudflare 会首先检查 Worker B 的缓存。如果命中,Worker A 直接接收响应且 Worker B 完全不执行 —— 没有数据中心跳转、没有数据库查询、没有渲染操作。

缓存命中路径为:用户 → 靠近用户的 Worker A → Worker B 的缓存命中 → 响应。只有在缓存未命中时才会进行数据访问。热门页面以靠近用户的代码执行速度运行,冷门页面在执行时仍能受益于靠近数据的执行位置。

您无需特殊架构即可实现这一效果。只需将应用编写为两个 Worker,通过服务绑定将它们指向彼此,在 Worker B 的 wrangler.jsonc 文件中启用缓存,即可完成。

如果你正在缓存一个返回用户特定数据的 Worker —— 例如一个根据登录用户显示不同内容的 API —— 你需要一种方法来确保一个用户永远无法看到另一个用户的缓存响应。标准解决方案是“不要缓存经过身份验证的请求”,Cloudflare 对授权头的自动绕过功能正是如此实现的。但“不缓存任何内容”会完全放弃性能优势。

Workers Cache 通过将调用者的 ctx.props 作为缓存键的一部分来解决这个问题。当一个 Worker 通过服务绑定调用另一个 Worker 并传递包含用户 ID、租户 ID 或其他任何标识符的 ctx.props 时,不同 props 的调用者会获得独立的缓存条目。一个用户的响应永远无法泄露到另一个用户的缓存中。

ts
import { WorkerEntrypoint } from "cloudflare:workers";

interface Props { userId: string; }

export default class Backend extends WorkerEntrypoint<Env, Props> {
  async fetch(request: Request): Promise<Response> {
    // `ctx.props.userId` 是缓存键的一部分。用户 A 和用户 B
    // 请求相同 URL 时会获得独立的缓存条目。
    const { userId } = this.ctx.props;
    const data = await loadUserData(userId);

    return new Response(JSON.stringify(data), {
      headers: {
        "Content-Type": "application/json",
        "Cache-Control": "public, max-age=300",
      },
    });
  }
}

典型模式是在网关 Worker 中对请求进行身份验证,剥离授权头,将已验证用户的 ID 设置到 ctx.props 中,然后调用缓存的后端 Worker。网关会在每个请求中运行(它必须如此才能进行身份验证),但昂贵的后端处理仅在该用户尚无缓存条目时运行。经过身份验证的 API 从“无法缓存”变为“按用户缓存且完全安全”,缓存键会为你实现隔离。文档在《使用 ctx.props 的多租户安全性》和《按用户身份验证的响应示例》中详细介绍了这一过程。

其他 CDN 强迫你在正确性和命中率之间做出选择:按每个用户的令牌作为缓存键,或让每个请求都回源进行身份验证。Workers Cache 让你可以在边缘共享缓存的 API 响应,同时保留每个请求的身份验证边界。我们不知道还有哪家 CDN 将这种能力作为认证多租户 API 的内置模型提供。我们对此感到非常自豪。

每个 Worker 入口之间的缓存

我们认为 Workers Cache 最具突破性的部分是它在每个 Worker 入口之间建立缓存,而这一点在你将其视为“恰好适用于 Worker 前端的 CDN 缓存”时最难察觉。

Workers Cache 位于每个 Worker 入口之前 —— 默认导出、每个命名的 WorkerEntrypoint,以及通过 ctx.exports 在同一个 Worker 的入口之间进行的每次调用。最后这一点改变了你能构建的内容。

当一个入口通过 ctx.exports 调用另一个入口时,缓存会以与浏览器请求相同的方式处理该调用。命中时会返回缓存响应且被调用方永远不会运行。未命中时会运行被调用方并将其响应存储在自己的缓存键下 —— 该键由被调用方的入口点、路径、查询字符串和 ctx.props 确定。调用方仍然会在每个请求中运行,但其传递给被调用方的任何内容都会独立地被记忆化。

你决定每个入口点的缓存策略。在 Wrangler 配置中,通过 exports 映射可以按名称("default" 是默认导出)为每个入口点单独开启或关闭缓存。将某个入口点纳入缓存范围,即可缓存其生成的响应;将其排除缓存范围,该入口点将在每次请求时运行。网关或路由器入口点(任何负责认证、规范化或分发的入口点)应排除缓存,以确保其始终运行,且其输出永远不会从缓存中提供。

这为你提供了一个可组合的基础单元。你可以将 Worker 编写为多个小型入口点的链式结构——认证、规范化、路由、昂贵的读取操作、数据层——并让 Workers Cache 在你需要的任何位置插入其中。每个被缓存的入口点都是一个独立的缓存单元,拥有自己的缓存键、自己的 TTL 和自己的标签命名空间用于清除缓存。关于缓存的任何配置需求——何时运行、基于什么键、何时失效——都可以通过普通 Worker 代码实现:调用哪个入口点、转发什么请求、传递什么 ctx.props、设置什么 Cache-Control。

为了更具体地说明,这里有一个单一 Worker,它实现了其他平台上难以轻松组合的三项功能:为每个请求进行认证、通过多租户安全的缓存键缓存昂贵的后端服务、并在数据变更时使缓存失效。

缓存配置按入口点进行。网关必须在每次请求时运行——既为了认证,也因为缓存的网关响应会跳过认证检查——因此我们禁用默认入口点的缓存,仅在内部入口点启用缓存:

json
{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2026-05-01",
  "cache": { "enabled": true },
  "exports": {
    // 网关在每次请求时运行——不要缓存它。
    "default": { "type": "worker", "cache": { "enabled": false } },
    // 缓存昂贵的内部入口点。
    "CachedBackend": { "type": "worker", "cache": { "enabled": true } }
  }
}
ts
import { WorkerEntrypoint } from "cloudflare:workers";

interface Env { API_TOKEN: string; }
interface Props { userId: string; }

// 内部入口点:执行昂贵的操作。Workers Cache 位于此入口点之前
// 当命中缓存时,此代码永远不会运行。
export class CachedBackend extends WorkerEntrypoint<Env, Props> {
  async fetch(request: Request): Promise<Response> {
    // ctx.props.userId 是缓存键的一部分,因此每个用户都会单独缓存。
    const { userId } = this.ctx.props;
    const data = await loadExpensiveData(userId);

    return new Response(JSON.stringify(data), {
      headers: {
        "Content-Type": "application/json",
        "Cache-Control": "public, max-age=300, stale-while-revalidate=3600",
        "Cache-Tag": `user:${userId}`,
      },
    });
  }

  // 使用户的缓存响应失效。purge() 的作用域限定在调用它的入口点,
  // 因此它必须在 CachedBackend 内运行——即拥有该缓存响应的入口点。
  async invalidate(userId: string): Promise<void> {
    await this.ctx.cache.purge({ tags: [`user:${userId}`] });
  }
}
ts
// 外层入口点:每个请求都会运行以进行身份验证和路由。
// 在 Wrangler 配置(上方)中已禁用其缓存,因此它始终
// 运行,且缓存命中不会跳过身份验证检查。
export default {
  async fetch(request, env, ctx): Promise<Response> {
    const userId = await authenticate(request, env);
    if (!userId) return new Response("Unauthorized", { status: 401 });

    // 在写操作时从拥有该用户的入口点
    // 使该用户的缓存失效。
    if (request.method === "POST") {
      await handleWrite(request, userId);
      await ctx.exports.CachedBackend.invalidate(userId);
      return new Response("OK");
    }

    // 对于读操作:移除 Authorization(否则 Cloudflare 的自动
    // 绕过机制会触发且无法缓存),然后通过缓存
    // 后端处理,将已认证用户身份通过 ctx.props 传递。
    const forwarded = new Request(request);
    forwarded.headers.delete("Authorization");

    return ctx.exports.CachedBackend.fetch(forwarded, {
      props: { userId },
    });
  },
} satisfies ExportedHandler<Env>;

整个系统是一个 Worker。一个源文件。一次部署。但存在两个执行阶段——通过一个小型 exports 块,网关禁用缓存而后端启用缓存——两者之间有一个缓存层,按用户键值存储,通过写操作路径失效,且在后台刷新期间提供陈旧数据。缓存阶段不是外部附加的组件,而是程序的一个层级,通过代码实现。

这种模式具有开放性。相同的架构可以用于:

  • 缓存 Durable Object。将 Durable Object 包裹在入口点后,对响应设置 Cache-Control,读取操作在命中时不再访问 Durable Object。写操作直接发送到 DO 并通过标签清除缓存。DO 会完全不知道缓存的存在。
  • 在 Vary 前标准化 Accept-Encoding。外层入口点从 request.cf.clientAcceptEncoding 恢复原始编码(Cloudflare 前端已为缓存效率进行了标准化),然后转发到一个根据真实值进行 Vary 的缓存入口点。命中率保持高位;客户端获得正确的编码。
  • 在缓存前剥离跟踪参数。外层入口点规范化 URL——或在 ctx.exports 调用时通过 cf.cacheKey 设置自定义缓存键——使缓存的内层入口点仅看到规范化形式,所有 ?utm_source=anything 都会折叠为一个缓存条目。

进行堆叠。一个 Worker 可以拥有:认证和路由的外层入口点、剥离跟踪参数并恢复编码头的规范化入口点、面向 Durable Object 的缓存入口点、以及一个面向未认证公共 API 的独立缓存入口点——每个入口点通过你未显式配置但决定放置位置的缓存阶段连接。文档中的示例页面详细演示了其中多个完整案例。

我们不知道还有其他平台能提供这种能力。CDN缓存位于源站之前,函数平台运行函数。我们不知道还有其他平台能为你提供一个缓存,它嵌入在单个可部署单元内部,在应用各部分之间,每个缓存阶段的配置都由其两侧的代码控制。这就是Workers缓存的特性。由于它可以与平台已提供的所有功能无缝集成——智能部署、持久对象、服务绑定、ctx.props和ctx.exports——你可以构建的模式是开放且无边界的。在这篇文章中,我们仅仅触及了表面。

在你的框架中获得原生支持

如果你使用Astro进行开发,Cloudflare适配器会为你自动配置Workers缓存。只需在配置中添加cacheCloudflare提供者:

code
// astro.config.mjs
import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";
import { cacheCloudflare } from "@astrojs/cloudflare/cache";

export default defineConfig({
  adapter: cloudflare(),
  output: "server",
  experimental: {
    cache: { provider: cacheCloudflare() },
    routeRules: {
      "/products/*": { maxAge: 300, swr: 3600, tags: ["products"] },
      "/blog/*":     { maxAge: 60,  swr: 86400, tags: ["blog"] },
    },
  },
});

该适配器会启用缓存,在Astro生成的响应中设置正确的头部,附加用于失效的Cache-Tag值,并提供cache.invalidate()工具用于内容变更时清除标签。选择使用服务端渲染的Astro页面会自动获得上述的"渲染一次,缓存,后台刷新"流程——无需每条路由的配置,也无需学习特定框架的运行时层。

我们正在与其他框架的维护者合作,将相同的集成方案推广到更多框架。如果你为Cloudflare构建框架适配器,Workers缓存API正是你期望的样子——基于头部的配置、程序化清除、无需建模特定平台的概念。

在同一个仪表板上查看缓存状态

只有当你能观察到缓存行为时,缓存才有价值。现在Workers可观测性仪表板会展示每个调用的缓存命中信息:

你可以按Worker查看:

  • 缓存命中率随时间的变化。启用缓存后,你希望这个数值呈上升趋势。
  • 命中、未命中、更新、绕过等指标的细分。如果命中率较低,这里能帮你找出原因——是太多BYPASS响应(因为某些操作设置了Cookie?),还是太多MISS响应(因为缓存键的分区比预期更多?),或是太多UPDATING响应(因为max-age设置比流量间隔更短?)。

由于这些信息与Worker的其他可观测性指标(日志、异常、CPU时间、请求数)显示在同一个仪表板上,你无需在查看区域和Worker之间切换上下文就能理解发生了什么。

计费

缓存命中不会触发Worker执行,也不会产生CPU时间费用。但它们会计入标准的Workers请求速率,与任何其他调用相同。缓存未命中和绕过会正常计费——请求+CPU时间,与未使用缓存时完全一致。

| 结果 | 请求费用 | CPU时间费用 | |--------------|----------|-------------| | 缓存命中(Worker不运行) | 标准费率 | 不计费 | | 缓存未命中(Worker运行) | 计费 | 计费 | | 缓存绕过(Worker运行) | 静态资源请求 | Worker到Worker调用 | 若Worker运行则计费 |

没有单独的 Workers Cache SKU,也没有按每GB计费的缓存存储费用。分层缓存、清除、stale-while-revalidate 以及上述分析功能均已包含在内。如果一个请求本应触发 Worker 执行,但 Workers Cache 直接命中并返回了缓存结果,您仍需支付标准请求费率,但该请求不会产生 CPU 时间费用。因此,这种缓存命中成本低于在 Worker 中渲染相同响应。

需要注意的一点是:当启用缓存时,原本免费的请求(静态资源请求以及通过服务绑定或 ctx.exports 实现的 worker-to-worker 调用)将按标准请求费率计费,因为每个请求现在都需要查询 Worker 前的缓存。

下一步计划

我们已明确的下一步改进方向包括:

  • 与 Smart Placement 的智能协同部署。目前,Cloudflare 分别独立选择上层缓存节点和 Smart Placement 目标节点。在完全未命中时,请求可能需要在 Cloudflare 不同节点间往返两次:一次查询上层缓存,另一次在靠近数据的节点执行 Worker。我们正在协调这两个决策过程,使未命中场景下的长距离传输仅发生一次。
  • 提高响应大小限制。上线初期,所有响应均遵循 Free 计划的缓存大小限制(512 MB),无论您的账户类型。这是临时措施——完成若干部署步骤后,各计划的标准缓存限制将正式生效。
  • 增加更多框架集成。Astro 已内置 Workers Cache 集成。我们正在与各框架维护者合作,通过 Vinext 等方式为 TanStack Start、Next.js 等框架添加类似集成。
  • 提供标记缓存响应过期的 API。ctx.cache.purge() 可从缓存中移除匹配响应。我们正在研究 ctx.cache.invalidate() API,使匹配响应表现为已过期,这样下次请求仍可通过 stale-while-revalidate 机制获取快速的陈旧响应,同时 Worker 在后台刷新缓存。

立即体验

Workers Cache 现已向所有计划的 Worker 开放使用。

要开始使用,请在 wrangler.jsonc 中添加 "cache": { "enabled": true },重新部署并开始设置 Cache-Control 头。Workers Cache 文档将带您全面了解所有功能——包括快速入门、缓存键、清除、组合模式示例和调试等内容。

过去 Worker 始终部署在缓存前端。现在,Worker 也可以部署在缓存后端。根据需要选择任意一侧部署——或通过服务绑定同时部署在两侧。

我们迫不及待想看到您用 Workers Cache 构建的创新应用。

[if astro]>server-island-start<![endif]

Cloudflare Workers

缓存

分层缓存

性能

开发者

无服务器计算 /