技术指南

CDN 缓存上线:旁路、命中、失效与回滚

把 CDN 缓存上线拆成旁路、缓存键、命中证据、精确失效与可验证回滚。

CDN 缓存上线不是打开一个“加速”开关。发布前要证明哪些请求可共享、哪些必须旁路、缓存键如何区分对象、失效后拿到什么新版本,以及规则出错时怎样恢复。命中率只能说明流量分布,不能替代内容正确性和会话隔离。

来源覆盖整套 Cloudflare 功能,本文只解决缓存发布

归档来源把 CDN 选型、Cloudflare 接入、Tunnel、R2、代理边界、缓存、TLS、安全功能、优选 IP、监控和套餐限制放在一篇文章里。可保留的是区分动态/静态内容、观察缓存响应、精确清理和保护源站的方向;固定价格、套餐配额、供应商排名、80% 命中率目标、通用超时、优选 IP 绕行、性能百分比和防护保证均不进入本文。Tunnel、R2 与源站收口已有独立指南,这里只形成一次可失败、可回滚的缓存发布。

先按响应类别写缓存契约

请求类别默认策略必须保留的证据主要风险
指纹静态资源公开、长 TTL、immutable内容摘要、版本 URL、MISS 后 HIT同 URL 覆盖导致旧内容长期存在
公开 HTML短 TTL 或重验证;先 canaryCache-Control、验证器、版本标记发布延迟、错误页面被共享
公开 API只在响应与键都明确时缓存方法、查询参数、Vary 与响应 schema不同参数或语言串数据
登录与用户态旁路,不进入共享缓存Cookie/Authorization 测试和用户 A/B 响应跨用户泄漏
错误与重定向按状态逐项决定状态码 TTL、Location、故障样本把暂时故障放大到所有访客

RFC 9111 区分存储、复用、验证与失效。`no-store` 表示不应存储,`private` 限制共享缓存,`no-cache` 要求复用前验证而不是“完全不缓存”。不要把浏览器缓存 TTL、共享边缘 TTL 和应用数据新鲜度混成一个数。

从真实路由和响应头盘点,而不是从文件后缀猜

替换为自己的公开与受控测试路由。响应头可能含 Cookie 和内部信息;不要把真实凭据写入命令或报告
base=https://www.example.com
dot=.

for path in \
  /assets/app.4f91c2.css \
  / \
  /api/catalog?page=1 \
  /account \
  /account/avatar${dot}jpg; do
  curl --silent --show-error --dump-header - --output /dev/null \
    "${base}${path}" \
    | sed -n '/^HTTP\/|^Cache-Control:|^CDN-Cache-Control:|^Vary:|^ETag:|^Last-Modified:|^Set-Cookie:|^CF-Cache-Status:|^Age:/Ip'
done

对每条路由记录方法、状态、认证条件、Cookie、查询参数、响应类型、大小、更新方式和泄漏后果。Cloudflare 的默认缓存行为会参考方法、扩展名、状态和源站头;显式 Cache Rule 又可能改变资格与 TTL,所以“默认不会缓存 HTML”不能成为用户态安全边界。

让源站先表达可缓存性

响应意图示例 Cache-Control发布约束
指纹资源public, max-age=31536000, immutableURL 必须随内容变化;不原地覆盖
共享页面public, max-age=0, must-revalidate提供 ETag 或 Last-Modified,并验证重验证路径
浏览器短存、边缘另定按已审 CDN 头与规则配置明确哪个头控制哪个缓存,不依赖隐式优先级
用户态或敏感响应private, no-store同时以规则旁路;不得靠响应头单点防守

截至 2026-08-17,Cloudflare 当前默认行为不缓存带 `private`、`no-store`、`no-cache`、`max-age=0` 或 `Set-Cookie` 的响应,也不缓存非 GET 请求;但 Edge TTL、Cache Rules、Cache Response Rules 和 Origin Cache Control 会改变最终行为。把源站头当契约,把边缘规则当可审覆盖,并在实际 zone 上重新验证。

先发布宽旁路,再添加窄缓存资格

  1. 第一条规则旁路登录、账号、结算、管理、预览、webhook 和其他用户态路径。
  2. 对 Authorization、会话 Cookie 和非 GET/HEAD 方法建立独立旁路条件。
  3. 只有已盘点的静态命名空间或公开响应类进入 Eligible for cache。
  4. 多个规则可能同时命中;Cloudflare 当前同一设置由最后匹配规则取胜,因此保存规则顺序和 Trace 结果。
  5. 不要用全站 Cache Everything 再期待源站偶然阻止敏感响应。

缓存键只包含真正改变表示的维度

维度何时进入键省略或加入的代价
Host + path通常始终需要跨主机碰撞或无法复用
query参数改变内容时保留;追踪参数可审后排除错误合并或键空间爆炸
Accept-Encoding由 CDN 正确处理压缩变体内容编码不匹配
语言/设备只有服务端确实返回不同表示时变体泄漏或命中率碎片化
Cookie/header公开内容一般先旁路,而不是把身份塞进共享键高基数、隐私数据进入键、清理困难

Cloudflare 当前自定义键可控制查询字符串、头、Cookie、Host 与用户特征,且选项随方案而异。每增加一个维度都增加观测与失效成本。若按自定义键缓存,按 URL 清理时也必须能指定形成该键的头;否则控制台中的单文件清理可能没有清到目标变体。

阻断伪静态路径和类型错配

Web Cache Deception 利用源站把账号页与追加图片扩展名后的路径当成同一动态路由,而边缘把后者误认为静态资源。修复应从路由严格 404、用户态旁路、响应 `private, no-store` 和测试异常后缀开始。Cloudflare Cache Deception Armor 可检查 URL 扩展名与返回 Content-Type 是否相符,但其保护也会受 Origin Cache Control 或 Edge TTL 覆盖,不能成为唯一防线。

把响应头解释成一次决策链

CF-Cache-Status当前含义下一步
HIT对象来自 Cloudflare 缓存核对 Age、对象版本、键与用户隔离
MISS未找到缓存对象并访问源站再次请求;确认是否应写入
DYNAMIC请求阶段不具备缓存资格若这是用户态可接受;否则查规则匹配
BYPASS请求先具备资格,响应阶段决定不缓存检查 Cache-Control、Set-Cookie、Authorization
EXPIRED / REVALIDATED对象过期并访问源站或验证后复用核对验证器、延迟和新鲜度
STALE / UPDATING过期对象在失败或后台更新窗口被服务确认业务是否允许旧内容及最大窗口
NONE/UNKNOWN响应没有经过常规缓存决策检查 Worker、WAF、重定向和 Trace

`Age` 只在缓存响应上出现,并会在重验证、清理或驱逐后重新计算。一次 HIT 不能证明所有 POP、所有变体或后续发布正确;一次 MISS 也不等于配置失败。保存 UTC 时间、请求类、规则版本、URL、关键请求头、状态、缓存头、响应摘要和源站请求计数。

按请求类别做冷、热和隔离测试

只对自己的资源测试。查询参数是否进入键必须与实际规则一致;不要用随机参数制造无意义 MISS
url=https://www.example.com/assets/app.4f91c2.css

for n in 1 2 3; do
  curl --silent --show-error --dump-header "headers-$n.txt" \
    --output "body-$n.bin" "${url}?cache-test=stable"
  sha256sum "body-$n.bin"
  sed -n '/^HTTP\/|^Cache-Control:|^CF-Cache-Status:|^Age:/Ip' \
    "headers-$n.txt"
done
  • 指纹资源应先出现可解释的冷路径,再在同一键上出现内容摘要相同的 HIT。
  • 改变一个会影响表示的查询参数,必须得到独立对象;改变已明确排除的追踪参数,才允许复用。
  • 匿名、用户 A、用户 B 分别请求用户态与伪静态路径,响应标识不能交叉且不得出现共享 HIT。
  • 对 404、301/302、206 Range 和压缩变体逐项检查,不从 200 的结果外推。
  • 从至少两个实际用户地区复测;本地夹具不能证明边缘 POP、Tiered Cache 或传播时间。

优先用不可变 URL,失效按最小范围执行

指纹资源随内容生成新 URL,减少对全局清理的依赖。必须清理可变对象时,先确定对象对应的完整键,再按 URL、标签、主机或前缀选择最小范围。Cloudflare 当前 API 支持按 URL、tag、host、prefix 或全量清理;使用自定义键时,URL 清理需要包含形成键的相关头。

清理请求成功只是控制面接受,不是内容已经更新的证明。随后用同一键取得新响应,保存缓存状态、Age、版本标记与正文摘要。按 tag 清理后通常观察到 MISS;启用 Tiered Cache 时也可能看到 EXPIRED。不要把“Purge Everything”当常规发布步骤,它会同时增加源站回填压力。

一次只改变资格、键或 TTL 中的一项

  1. 导出当前规则、顺序、缓存配置和关键响应头,计算配置摘要。
  2. 以 hostname、路径或内部 canary 标记限制候选范围,并用 Trace 确认命中规则。
  3. 先证明用户态旁路和 A/B 隔离,再证明静态对象冷转热。
  4. 观察源站请求量、按类别命中、4xx/5xx、延迟、错误页面与内容版本。
  5. 扩大范围时保持同一判定标准,不同时修改 DNS、源站路由、压缩和应用发布。

回滚规则后,还要清掉候选产生的错误对象

触发条件包括用户态出现 HIT、跨身份正文摘要相同、错误状态被共享、版本长期不前进、源站回填过载或规则 Trace 与预期不同。立即停止扩大,恢复上一份规则及顺序;再精确清理候选期间产生的错误对象和变体,验证旧行为与正确内容均恢复。只回滚规则不会删除已经写入的缓存对象。

隔离演练验证了旁路、键、版本与清理

VPScope 使用 Ubuntu Nginx 1.24.0 与一次性 Python 源站,只监听 127.0.0.1 高端口。候选缓存显式只处理 GET/HEAD,按完整 URI 区分查询参数,并对 Authorization、会话 Cookie、`private/no-store` 与 `Set-Cookie` 响应旁路;`X-Cache-Status` 暴露本地 Nginx 的 MISS/HIT/BYPASS。

指纹静态资源第一次 MISS、第二次 HIT,源站计数保持 1;查询参数变化得到独立 MISS。用户 A、用户 B 和追加图片扩展名的伪静态账号路径都保持 BYPASS 且正文不交叉。新指纹 URL 先 MISS 再 HIT;清理临时缓存文件后旧 URL 再次 MISS。该结果只证明本地 Nginx 夹具的 HTTP 缓存契约,不证明 Cloudflare Cache Rules、POP、Tiered Cache、Trace 或全局清理。进程、端口和临时缓存均由 EXIT trap 清理。

CDN 缓存上线前的可复查清单

  • 每个请求类别都有共享范围、键、TTL、验证器、失效和回滚契约。
  • 用户态、Authorization、会话 Cookie、非安全方法和敏感路径默认旁路。
  • 源站为公开、重验证与敏感响应发出明确缓存头,边缘覆盖范围有版本记录。
  • 规则顺序已保存,多个匹配规则的最终值通过当前 Trace 核对。
  • 查询参数、Host、语言、压缩和其他变体只在改变表示时进入键。
  • 伪静态路径、错误 Content-Type、404、重定向、Range 与压缩变体分别测试。
  • CF-Cache-Status、Age、Cache-Control、Vary、验证器、响应摘要和源站计数共同解释结果。
  • 冷 MISS、热 HIT、匿名/用户 A/用户 B 隔离和多地区 canary 均符合预期。
  • 发布优先使用指纹 URL;精确清理后用同一完整键证明新内容已返回。
  • 恢复旧规则、清理候选错误对象、控制源站回填和逐步重开已经演练。

返回知识库