如果缓存行为不符合预期,Cf-Cache-Status 响应头是你首先要查看的地方。每个响应都会携带该头,其值会准确告诉你该请求发生了什么。
向同一 URL 发送两次请求并比较响应头:
curl -I https://my-worker.example.workers.dev/api/users/42
curl -I https://my-worker.example.workers.dev/api/users/42将状态值与以下场景对照。
Cf-Cache-Status 不存在。请确认你的 wrangler 版本为 4.69.0 或更高,并且 wrangler.toml 或 wrangler.jsonc 中该 Worker 已设置 cache.enabled = true。
Cf-Cache-Status 在每次请求时都是 MISS、DYNAMIC 或 BYPASS。缓存未存储任何内容,或 bypass 规则被触发。
检查 Cache-Control 响应头。 响应必须携带使其可缓存的指令:
public, max-age=N— 在 Cloudflare 和浏览器中缓存N秒。
带有 Cache-Control: private 或 no-store 的响应不会被存储,Cf-Cache-Status 为 BYPASS。
带有 Cache-Control: no-cache 的响应_会_被存储,但 Cloudflare 会将每次后续请求视为 stale,并在提供内容前向你的 Worker 发起校验。具体的 Cf-Cache-Status 取决于是否同时设置了 stale-while-revalidate:
- 仅使用
Cache-Control: no-cache时,每次后续请求都会触发 inline revalidation。若 Worker 返回304 Not Modified,Cf-Cache-Status为REVALIDATED(响应体来自缓存);若 Worker 返回新的200,则为EXPIRED(响应体被替换)。 - 使用
Cache-Control: no-cache, stale-while-revalidate=N时,缓存的响应体会立即提供,Worker 在后台运行。在 SWR 窗口内,Cf-Cache-Status为UPDATING。
若你希望获得长期缓存命中,请改用 max-age。请参阅 no-cache 不是 bypass。
如果响应完全没有 Cache-Control 响应头,行为取决于状态码:Workers Caching 会应用 RFC 9111 启发式 freshness ↗,并按启发式 TTL 缓存默认可缓存的状态码 — 例如,200 缓存 2 小时,404 缓存 3 分钟。完整默认 TTL 表请参阅配置参考中的没有 Cache-Control 响应头的响应仍会被缓存。若不希望任何默认行为生效,请在响应上显式设置 Cache-Control。
检查请求方法。 只有 GET 和 HEAD 请求会被缓存。其他所有方法均为 BYPASS。同一 URL 的 GET 和 HEAD 请求共享同一缓存条目 — 请参阅缓存键了解 Cloudflare 如何处理任一方法填充缓存。
检查自动 bypass 条件。 在以下情况下 Cloudflare 会 bypass 缓存:
- 响应包含
Set-Cookie响应头。 - 请求包含
Authorization响应头,除非响应显式设置了Cache-Control: public、must-revalidate或s-maxage。
若 Worker 无条件设置 Set-Cookie(例如在每个响应上设置 session cookie),该响应永远不会被缓存。请从可缓存响应中移除 cookie,或将设置 cookie 与可缓存响应拆分到不同路由。
检查状态码。 Workers Caching 遵循 RFC 9111 ↗。默认不可缓存的状态码(例如 401、403、500)不会被存储,除非你显式用可缓存指令标记它们。
少数状态码即使带有显式 Cache-Control 也永远不会被缓存:
520–526被视为 Cloudflare failsafe 响应,永远不会被存储。- Worker 返回的
206 Partial Content不会被存储。Workers Caching 会通过从你的 Worker 获取完整响应体并从缓存条目中切片来处理Range请求 — 若 Worker 自行返回206,该响应被视为不可缓存。请改为返回完整的200。请参阅Range请求。
第一次请求时 Cf-Cache-Status 为 MISS,但后续请求仍为 MISS。
缓存很可能被分区了。 缓存键包含请求路径、目标 entrypoint 以及调用的 ctx.props。对你来说看起来相同的两条请求,若上述任一因素不同,可能产生不同的缓存键。
常见原因:
- 请求之间的 URL 路径或查询字符串不同(连尾部斜杠也有影响)。
- 调用 Worker 为每次请求传入不同的
ctx.props— 例如不同的用户 ID。 - 请求命中同一 Worker 的不同命名 entrypoint。
Cloudflare 目前不暴露缓存键的组成,因此无法直接查看计算后的键。请改为逐项核对缓存键中列出的组件,确认两次请求的每一项都相同。
在默认配置下这是预期行为。默认情况下,Worker 版本是缓存键的一部分,因此每个新版本都从冷缓存开始,无法复用上一版本的缓存响应。部署后的首批请求为 miss,新版本的缓存逐渐填充后,命中率会恢复。
若你频繁部署且响应在部署之间很少变化,可启用 cache.cross_version_cache 以跨版本共享缓存响应,避免每次部署都重置缓存。代价是会影响缓存的变更不再立即生效 — 见下文。
默认情况下部署会立即生效,因为 Worker 版本是缓存键的一部分,新版本从冷缓存开始。若仍看到来自旧版本的响应,说明已启用 cache.cross_version_cache,它会在版本间共享缓存条目。要在保持 cross_version_cache 开启的同时强制部署生效:
- 部署后调用
ctx.cache.purge({ purgeEverything: true })。 这是最简单的方法。 - 使用版本元数据绑定(binding)为每个缓存响应打上产生该响应的版本标签,回滚时 purge 该标签。请参阅按版本 purge。
若源数据已变更但请求仍返回 stale 内容:
- 检查 TTL。 响应会在
max-age秒内保持缓存。你看到的可能是仍在 freshness 窗口内的响应。 - Purge 受影响的响应。 使用
ctx.cache.purge()配合标签或路径前缀使特定条目失效。请参阅Purge 缓存。 - 在写入时添加标签。 若未设置
Cache-Tag响应头,则无法按标签 purge。为缓存响应添加标签、部署,新条目写入后即可被 purge。
若使用 ctx.props 承载按调用方的授权上下文,这不应发生。若发生了,以下之一为真:
- 你使用未纳入缓存键的响应头或查询参数对调用方进行身份验证。将授权输入移入
ctx.props。请参阅使用ctx.props实现多租户安全。 - 你通过 service binding 调用时使用了特定于用户的查询参数,但该参数不存在。查询字符串是缓存键的一部分;请确保每个调用方的请求路径确实不同。
UPDATING 表示响应从 stale 缓存中提供,Worker 在后台运行以刷新它。使用 stale-while-revalidate 时这是预期行为。
若 UPDATING 出现频率超出预期:
- 你的
max-age短于请求到达频率。max-age过期后到达的每个请求都会触发 revalidation。 - 使用
max-age=0, stale-while-revalidate=<large>时,每个请求都会触发 revalidation。这是「始终从缓存提供」的行为,而非「不运行 Worker」。请参阅选择 TTL 和 stale-while-revalidate 值。
UPDATING 仅在以下全部条件成立时才会发出:
- 存在缓存条目且已超出 freshness 窗口(stale)。
- 响应携带
stale-while-revalidate=N,且请求在条目变 stale 后的N秒内到达。 - 响应未同时携带
s-maxage、must-revalidate或proxy-revalidate。
若任一条件不成立,stale 条目的请求会走 inline revalidation,产生 EXPIRED(Worker 返回新响应体)或 REVALIDATED(Worker 返回 304 Not Modified)。
UPDATING 不出现的常见原因:
- 响应上没有
stale-while-revalidate指令。 默认 SWR 窗口为0,因此没有显式指令时,每个 stale 请求都会在前台 revalidate。 - 存在
s-maxage、must-revalidate或proxy-revalidate。 根据 RFC 9111 §4.2.4 ↗,这些指令禁止提供 stale 内容,因此 Cloudflare 在存在任一指令时会禁用stale-while-revalidate(以及stale-if-error)。若希望 stale-serving 生效,请用max-age设置 edge freshness 窗口。 - SWR 窗口已过期。 若响应使用
max-age=60, stale-while-revalidate=120,条目变 stale 后的 120 秒内你会看到UPDATING。之后到达的请求会回退到 inline revalidation。
STALE 表示 Cloudflare 提供了先前缓存的响应,因为本应刷新它的请求上 Worker 出错了 — 例如 Worker 抛出异常、超时或返回 5xx 响应。这是 stale-if-error 行为。请参阅使用 stale-if-error 在出错时提供 stale 内容。
若出现 STALE 但未预期到:
- Worker 在缓存填充或 revalidation 时失败。 在 Workers 可观测性仪表板中检查本应产生新响应的请求上的错误。客户端看到 stale 响应而非
5xx,掩盖了真实的故障。 - 未显式设置
stale-if-error,且响应不包含s-maxage/must-revalidate/proxy-revalidate。 此时 Cloudflare 的默认行为是在 Worker 出错时无限期提供 stale 响应,只要缓存条目未被 purge。若希望错误快速暴露给客户端,请在Cache-Control中设置stale-if-error=0。详情请参阅使用stale-if-error在出错时提供 stale 内容。 - 正在提供先前部署的版本。 若已部署修复但
STALE仍持续出现,来自故障版本的缓存条目仍会在每次出错时被提供。Purge 受影响的条目,以强制从当前版本重新填充。
要在客户端可观测性中区分 STALE 与正常的 HIT,请与响应一并记录 Cf-Cache-Status — STALE 是唯一表明 Worker 正在失败而客户端未察觉的信号。
若响应过大无法缓存,Cloudflare 不会存储它。即使响应在其他方面看起来可缓存,每次请求的 Cf-Cache-Status 都会是 MISS。
各套餐的响应大小限制请参阅可缓存大小限制。请注意,发布时所有 Workers Caching 响应均受 Free 套餐大小限制 — 详情请参阅响应大小。
发布时,主要的调试入口是 Cf-Cache-Status 响应头,以及 Workers 可观测性仪表板中每次调用的缓存命中信息。