Workers 缓存在每个 Worker 中单独配置,通过 Wrangler 配置文件进行设置。启用后,缓存会应用于每次 fetch() 调用——包括终端用户请求、service binding 的 fetch() 调用,以及通过 ctx.exports 在入口点之间进行的 loopback fetch() 调用——除非你为特定入口点禁用缓存。自定义 RPC 方法 会绕过缓存。
这是 你的 Worker 缓存——通过 Worker 代码和 Wrangler 文件进行配置。Worker 完全通过以下方式控制其缓存:
- Wrangler 配置中的
cache.enabled标志,用于开启或关闭缓存。你可以按入口点覆盖此设置,并控制跨版本行为。 - Worker 在其响应上设置的
Cache-Control(以及cdn-cache-control、cloudflare-cdn-cache-control)标头,遵循 RFC 9111 ↗。 - 用于批量清除的可选
Cache-Tag响应标头,以及用于程序化失效的ctx.cache.purge()。
这就是全部的配置项。
在 Wrangler 配置中添加 cache 块:
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-17",
"cache": {
"enabled": true,
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-17"
[cache]
enabled = true将 cache.enabled 设置为 true 后,Cloudflare 会在每次 HTTP 请求时先检查缓存,再调用 Worker。这是每个入口点的默认行为;你可以通过 exports 按入口点覆盖。
cache 块接受两个字段:enabled(必填)和 cross_version_cache(可选)。任何其他字段均保留供将来使用,在 Wrangler 的未来版本中可能会导致验证错误。
要关闭缓存,将 cache.enabled 设置为 false(或移除 cache 块)并重新部署:
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-17",
"cache": {
"enabled": false,
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-17"
[cache]
enabled = false禁用缓存不会清除先前已缓存的响应——它只会阻止 Cloudflare 在后续请求中查询或填充缓存。如果稍后重新启用缓存,仍在 TTL 内的条目可以再次使用。如果你需要立即停止提供已缓存的响应,请在禁用后清除缓存。
cache.enabled 为整个 Worker 设置默认值,但一个 Worker 可以暴露多个入口点——默认导出以及任意数量的命名 WorkerEntrypoint 类——你可以为每个入口点独立开启或关闭缓存。使用 exports 映射,以入口点名称为键,"default" 指默认导出:
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-17",
"cache": {
"enabled": true,
},
"exports": {
// Opt the default entrypoint out of caching.
"default": { "type": "worker", "cache": { "enabled": false } },
// Keep caching on for the Admin entrypoint.
"Admin": { "type": "worker", "cache": { "enabled": true } },
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-17"
[cache]
enabled = true
[exports.default]
type = "worker"
[exports.default.cache]
enabled = false
[exports.Admin]
type = "worker"
[exports.Admin.cache]
enabled = true每个条目的格式为 { "type": "worker", "cache": { "enabled": <boolean> } }。按入口点设置的 cache.enabled 会覆盖该入口点的顶层 cache.enabled;未列出的入口点继承顶层值。你也可以在没有顶层 cache 块的情况下,仅为单个入口点启用缓存。
这样你可以选择性地为特定入口点启用或禁用缓存,而无需修改 Worker 代码:
- 将入口点退出缓存,使其在每次请求时都运行——这适用于网关或路由入口点,这类入口点负责认证、规范化或分发,本身不应从缓存中提供。这是构建网关模式的推荐方式:在网关入口点上禁用缓存,在网关通过
ctx.exports调用的内部入口点上启用缓存。 - 将入口点加入缓存,仅缓存返回可复用响应的特定入口点,其余部分保持不缓存。
cache 配置是 Worker 版本的一部分:
- 通过
wrangler deploy或wrangler versions upload上传的每个版本都会捕获其 Wrangler 配置中的cache.enabled值。 - 回滚到先前版本也会回滚该版本附带的
cache设置。 - 你可以使用渐进式部署,在将缓存应用于 100% 流量之前,先为一定比例的流量启用缓存。在从禁用缓存的版本渐进部署到启用缓存的版本期间,路由到旧版本的流量仍像以前一样不缓存,路由到新版本的流量会查询并填充缓存。默认情况下,Worker 版本是缓存键的一部分,因此两个版本会填充独立的缓存条目,不会提供彼此的响应——请参阅跨版本缓存。
默认情况下,Worker 版本是缓存键的一部分。每个已部署的版本都有独立的缓存,因此新部署从空缓存开始,永远不会提供先前版本写入的响应。这是默认行为,因为它是最容易理解的行为:新部署立即生效,你永远不会提供已被取代版本产生的响应。
代价是每次部署后缓存命中率都会重置。由于新版本无法复用先前版本的缓存响应,部署后的首批请求都是未命中,同时新版本的缓存正在填充。这是 Worker 缓存在部署后命中率下降的最常见原因。
如果你希望最大化缓存命中率,并愿意接受缓存相关变更的较慢推出,请将 cross_version_cache 设置为 true。然后缓存响应会在版本之间共享——一个版本写入的响应可以由后续版本提供,只要其 TTL 尚未过期:
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-17",
"cache": {
"enabled": true,
"cross_version_cache": true,
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-17"
[cache]
enabled = true
cross_version_cache = true频繁部署且大多数部署之间响应内容不变的高级用户应考虑启用 cross_version_cache——这样可以避免每次部署都丢弃温缓存。代价是部署不再使缓存失效:在更改响应内容后,较旧的缓存响应会继续提供,直到过期或你清除它们,并且在渐进式部署期间,两个版本共享一个缓存。当需要在启用 cross_version_cache 的情况下立即生效部署时,请在部署后清除缓存,或按版本标记响应——请参阅跨部署使缓存失效。
cross_version_cache 仅在启用缓存时生效。它适用于所有启用了缓存的入口点。
cache 块可以在顶层设置,并按环境覆盖。典型模式是在确认安全后在生产环境中启用缓存,同时保持 staging 不缓存以便调试:
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-17",
"cache": {
"enabled": false,
},
"env": {
"production": {
"cache": {
"enabled": true,
},
},
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-17"
[cache]
enabled = false
[env.production.cache]
enabled = true启用缓存后,你的 Worker 是 Cloudflare 缓存的源站。Worker 返回的响应上的标准 HTTP Cache-Control 指令决定 Cloudflare 是否缓存以及缓存多长时间。有关完整指令列表及其交互方式,请参阅 Cache-Control。
使用 max-age 控制响应被视为 fresh 的时长:
export default {
async fetch(request) {
const body = await renderPage(request);
return new Response(body, {
headers: {
"Content-Type": "text/html",
// Cached for 1 hour at Cloudflare's edge and in the browser.
"Cache-Control": "public, max-age=3600",
},
});
},
};
// Replace with your own rendering logic.
async function renderPage(request) {
return `<!doctype html><title>Home</title><h1>Hello</h1>`;
}export default {
async fetch(request): Promise<Response> {
const body = await renderPage(request);
return new Response(body, {
headers: {
"Content-Type": "text/html",
// Cached for 1 hour at Cloudflare's edge and in the browser.
"Cache-Control": "public, max-age=3600",
},
});
},
} satisfies ExportedHandler;
// Replace with your own rendering logic.
async function renderPage(request: Request): Promise<string> {
return `<!doctype html><title>Home</title><h1>Hello</h1>`;
}如果你需要浏览器和边缘使用不同的缓存时长,请使用 cdn-cache-control(或 cloudflare-cdn-cache-control)作为仅边缘指令,并使用 Cache-Control 控制浏览器看到的内容。请参阅下方的标头优先级。
当缓存响应变为 stale 时,stale-while-revalidate 允许 Cloudflare 立即返回 stale 响应,并在后台刷新:
export default {
async fetch(request) {
const data = { timestamp: Date.now() };
return new Response(JSON.stringify(data), {
headers: {
"Content-Type": "application/json",
// Fresh for 10 minutes; may be served stale for up to 1 minute
// while a background revalidation runs.
"Cache-Control": "public, max-age=600, stale-while-revalidate=60",
},
});
},
};export default {
async fetch(request): Promise<Response> {
const data = { timestamp: Date.now() };
return new Response(JSON.stringify(data), {
headers: {
"Content-Type": "application/json",
// Fresh for 10 minutes; may be served stale for up to 1 minute
// while a background revalidation runs.
"Cache-Control": "public, max-age=600, stale-while-revalidate=60",
},
});
},
} satisfies ExportedHandler;高缓存命中率和高 freshness 之间存在张力。后台重新验证隐藏了刷新缓存的延迟,但 Worker 在每次重新验证时仍会运行——这并非免费。
两种常见模式:
- 基本静态内容,对 stale 有少量容忍度。 使用较短的
max-age(例如 60 秒)和较长的stale-while-revalidate窗口(例如 3600 秒)。大多数请求是HIT;偶尔的请求会触发后台刷新。 - 高流量端点的「始终从缓存提供」。 使用
max-age=0, stale-while-revalidate=<large>。每个请求立即返回先前缓存的响应并触发后台刷新。Worker 在每次请求的重新验证中运行一次,因此 CPU 成本接近每次都运行 Worker。请求量下降时 freshness 也会下降——如果长时间没有请求,下一个请求会看到 stale 内容。
stale-if-error 允许 Cloudflare 在 Worker 刷新已过期的缓存条目失败时返回先前缓存的响应——例如抛出异常、超时或返回 5xx 响应时。这可以保护客户端免受 Worker 短暂故障的影响。
"Cache-Control": "public, max-age=600, stale-if-error=86400",当 Worker 正在生成 fresh 响应时,stale-if-error 没有作用。当 Worker 在刷新已过期条目失败时,Cloudflare 会在 stale-if-error 窗口内提供最后一次成功的缓存响应(Cf-Cache-Status: STALE)。真正的缓存未命中(没有先前的条目)无法从 stale-if-error 中受益,因为没有 stale 内容可提供——在这种情况下 Worker 错误会直接传递给客户端。
当存在多个缓存标头时,最具体的优先:
cloudflare-cdn-cache-control— Cloudflare 专用,优先级最高。由 Cloudflare 消费并从返回给客户端的响应中剥离。cdn-cache-control— CDN 专用指令的标准标头。Cloudflare 遵循并传递给下游 CDN。Cache-Control— 标准 HTTP 标头。Cloudflare 遵循并传递给客户端。
使用 cloudflare-cdn-cache-control 当你希望边缘 TTL 比暴露给浏览器的更长,且不想将指令泄露给下游时。
通常被调用方通过在其响应上设置 Cache-Control 来决定其响应如何缓存。当一个入口点通过 ctx.exports loopback 调用另一个已缓存的入口点时,调用方入口点可以通过在请求上设置 cf.cacheControl 来提供该调用的 Cache-Control 指令。
此处 Backend 入口点本身不返回 Cache-Control;默认入口点通过 ctx.exports 调用 Backend 时决定缓存策略:
import { WorkerEntrypoint } from "cloudflare:workers";
// Cached entrypoint. It does not set Cache-Control itself.
export class Backend extends WorkerEntrypoint {
async fetch(request) {
return new Response("Hello from the backend", {
headers: { "Content-Type": "text/html" },
});
}
}
// Gateway entrypoint. Caches the Backend's response for this call for
// 5 minutes, without the Backend needing to set Cache-Control itself.
export default {
async fetch(request, env, ctx) {
return ctx.exports.Backend.fetch(request, {
cf: { cacheControl: "public, max-age=300" },
});
},
};import { WorkerEntrypoint } from "cloudflare:workers";
// Cached entrypoint. It does not set Cache-Control itself.
export class Backend extends WorkerEntrypoint<Env> {
async fetch(request: Request): Promise<Response> {
return new Response("Hello from the backend", {
headers: { "Content-Type": "text/html" },
});
}
}
// Gateway entrypoint. Caches the Backend's response for this call for
// 5 minutes, without the Backend needing to set Cache-Control itself.
export default {
async fetch(request, env, ctx): Promise<Response> {
return ctx.exports.Backend.fetch(request, {
cf: { cacheControl: "public, max-age=300" },
});
},
} satisfies ExportedHandler<Env>;Cloudflare 将 cf.cacheControl 视为用于缓存该调用中被调用方响应的可信 Cache-Control 指令。该值是标准 Cache-Control 字符串,遵循本页所述的相同指令语义——max-age、stale-while-revalidate、no-store 等。这样调用方入口点可以决定已缓存入口点的响应如何缓存,而无需修改该入口点的代码。
与自定义缓存键类似,cf.cacheControl 仅对账户内调用生效。请求跨越账户边界时 Cloudflare 会丢弃 cf 对象,因此一个账户中的调用方无法更改另一个账户中 Worker 的缓存方式。该指令对终端用户请求也没有作用,因为入站请求上的 cf 对象由 Cloudflare 填充而非客户端。
每个响应都携带 Cf-Cache-Status 标头,指示该请求的处理情况。最常见的值是 HIT、MISS、EXPIRED、REVALIDATED、UPDATING、STALE 和 BYPASS。有关完整值集及其含义,请参阅 Cloudflare 缓存响应。
Cache-Tag 响应标头将标签附加到缓存响应,以便稍后批量清除。Cloudflare 消费此标头并在响应到达客户端之前将其剥离。
export default {
async fetch(request) {
const html = `<!doctype html><title>Post</title>`;
return new Response(html, {
headers: {
"Content-Type": "text/html",
"Cache-Control": "public, max-age=3600",
"Cache-Tag": "blog,posts,post-123",
},
});
},
};export default {
async fetch(request): Promise<Response> {
const html = `<!doctype html><title>Post</title>`;
return new Response(html, {
headers: {
"Content-Type": "text/html",
"Cache-Control": "public, max-age=3600",
"Cache-Tag": "blog,posts,post-123",
},
});
},
} satisfies ExportedHandler;Cache-Tag 标头值是逗号分隔的标签列表。适用与 zone 缓存相同的限制——请参阅缓存标签限制获取完整列表。需要记住的最常见约束:
- 标签值必须是可打印 ASCII(
0x21–0x7E)——不能有空格、Unicode 或控制字符。 - 每个标签最多 1024 个字符。
- 一个响应最多可携带 1000 个标签用于清除。
- 清除时的标签匹配不区分大小写。
Foo和foo清除相同的响应集。
无效标签(超长、包含空格或包含非 ASCII 字符)在缓存存储期间会被静默丢弃——响应仍会与剩余有效标签一起缓存,但你无法检测哪些标签被丢弃了。如果这很重要,请在 Worker 返回前验证标签。
Workers 缓存继承 Cloudflare 的标准缓存绕过规则。最常见的触发条件:
- 响应包含
Set-Cookie标头(除非Cache-Control包含private="set-cookie"或no-cache="set-cookie",此时Set-Cookie会从缓存副本中剥离)。 - 请求包含
Authorization标头。仅当Cache-Control包含public、must-revalidate或s-maxage时才存储响应,遵循 RFC 9111 §3.5 ↗。 - 响应
Cache-Control标头包含private或no-store。
当以上任一条件适用时,Cf-Cache-Status 为 BYPASS,Worker 在每次请求时都会运行。
少数状态码永远不会被存储,即使设置了显式的 Cache-Control 指令:
520–526(Cloudflare 故障安全响应)被视为 transient 错误,永远不会被缓存。
Workers 缓存从已缓存的完整响应提供 Range 请求——Worker 无需实现字节范围切片。
当客户端发送 Range 请求时,Cloudflare 在调用 Worker 之前剥离 Range 标头,并向 Worker 请求完整正文。Worker 返回带有 Cache-Control 标头的正常 200 响应(与任何其他请求一样),Cloudflare 存储该完整响应,然后切片出请求的字节范围并以 206 Partial Content 响应(或范围无效时为 416 Range Not Satisfiable)返回给客户端。对同一 URL 的后续 Range 请求完全从缓存条目满足——不会调用 Worker,Cf-Cache-Status 为 HIT。
例如,对冷缓存的 GET 请求携带 Range: bytes=0-9 会在进入时产生 MISS(Worker 运行并返回完整正文),然后返回包含前 10 字节的 206。对同一 URL 的后续 GET Range: bytes=10-19 是 HIT,从缓存返回这 10 字节而不调用 Worker。
如果 Worker 自己返回 206 响应——例如因为在 Worker 内实现了 Range 处理——Cloudflare 将其视为不可缓存响应且不会存储。返回完整的 200,让 Workers 缓存处理范围切片。
当 Worker 返回 Vary 响应标头时,Cloudflare 为所列请求标头值的不同组合分别存储缓存变体,仅当存储的值与入站请求匹配时才返回变体。这实现了 RFC 9110 ↗ 和 RFC 9111 ↗ 中的缓存键计算。有关带示例代码的介绍,请参阅使用 Vary 进行内容协商。
Workers 缓存如何处理 Vary:
- 所有标头名称都会被尊重。 Worker 在
Vary中列出的任何标头名称都参与变体键。没有允许列表。 - 值按字面比较。 Cloudflare 在键入前不会规范化所列请求标头。
Accept-Encoding: gzip, br和Accept-Encoding: br, gzip会产生两个独立的变体,尽管它们在语义上相同。如果你需要将等价值折叠到同一变体,请在传递请求之前在网关 Worker 中规范化 Worker 看到的标头,或在设置Vary的 Worker 内部规范化它们。 Vary: *禁用缓存。 通配符 variance 无法从请求标头确定性地满足,因此响应被视为不可缓存,Cf-Cache-Status为BYPASS。- 变体共享单一清除标识。 按标签或路径前缀清除会一起使 URL 的所有变体失效。因此所有变体必须使用相同的
Cache-Tag值——为不同变体分配不同标签会导致清除不一致。 - 图像转换功能优先。 Polish 或 Image Resizing 产生的响应已生成自己的变体,
Vary在这些响应上会被忽略。
Worker 控制其自己的内容协商。Worker 在响应上设置的 Content-Encoding 就是 Cloudflare 存储并提供给后续请求的内容。
如果 Worker 需要向不同客户端返回不同编码,有两个选项:
- 在 Worker 内部选择一种规范编码。 根据
Accept-Encoding请求标头决定,对正文编码一次,并返回单一表示。对该 URL 的后续请求无论接受什么都会命中同一缓存条目。这产生最高的缓存命中率,但需要你决定为哪些客户端提供哪种编码。 - 对
Accept-Encoding使用 Vary。 按请求返回不同的Content-Encoding,并设置Vary: Accept-Encoding。Cloudflare 为 Worker 见过的每个不同Accept-Encoding值存储一个变体。由于比较是字面意义上的,以不同顺序或不同 quality factor 发送语义等价值的客户端会产生独立变体——在生成响应之前规范化Accept-Encoding(例如在网关 Worker 中)以控制缓存 fan-out。