直接答案:CDN 显示旧内容时,先区分旧内容来自浏览器缓存、Service Worker、CDN 边缘节点、源站 Nginx/FastCGI 缓存、应用页面缓存还是数据库。然后对同一 URL 记录响应体摘要、Cache-Control、Age、ETag、Last-Modified、CDN 缓存状态和命中节点。在哪一层第一次出现旧版本,就修哪一层。直接执行全站 Purge 会增加回源压力,也无法修复错误的缓存规则。
缓存问题最容易被“我这里已经新了”误判。不同地区、不同查询参数、登录状态、设备和 CDN 节点可能使用不同缓存键。验收必须从多个网络和无登录会话进行,并与源站直连结果对照。
六层缓存地图
| 层级 | 常见标志 | 如何旁路或验证 | 典型根因 |
|---|---|---|---|
| 浏览器内存/磁盘 | DevTools 显示 memory/disk cache | 无痕窗口、禁用缓存、改版本 URL | 本地缓存未过期 |
| Service Worker | 请求被 Worker 接管 | 查看 Application/Service Workers | 离线策略未更新 |
| CDN 边缘 | Age、供应商缓存状态头 |
指定节点/地区、Purge 单 URL | TTL、缓存键、节点传播 |
| 源站代理 | Nginx/FastCGI 缓存头或目录 | 直连源站、查缓存日志 | 源站仍缓存旧响应 |
| 应用缓存 | 插件、页面缓存、Redis | 应用调试、按 key 清理 | 缓存失效逻辑错误 |
| 数据层 | API/数据库本身旧 | 直接查询权威数据源 | 更新未提交、读副本延迟 |
第一步:给新旧版本一个可验证标记
不要凭肉眼比较整页。为测试版本记录构建号、发布时间或内容哈希,并选择一个具体 URL。响应体可以计算摘要;静态文件可在文件名中使用内容哈希。这样能回答“哪个节点返回哪个版本”,而不是争论页面看起来是否变化。
测试时保存时间、URL、查询参数、Cookie 状态、客户端地区、解析到的 CDN、响应头和摘要。涉及个性化页面时要保护 Cookie 和用户信息,不把授权响应拿去公开测试。
第二步:读取缓存响应头
Cache-Control 决定浏览器和共享缓存的基本行为,Age 表示响应在共享缓存中的驻留秒数,ETag 与 Last-Modified 可参与重新验证。CDN 还会增加自己的 HIT/MISS/BYPASS/DYNAMIC 等状态头,具体含义以供应商文档为准。
常见误区包括:
- HTML 设置了很长
max-age,发布后用户浏览器仍持有旧页; - 只设置浏览器
no-cache,但 CDN 页面规则覆盖了源站; Set-Cookie、Authorization或自定义规则导致本应缓存的静态资源 BYPASS;- 查询参数被忽略,不同版本 URL 落到同一个缓存键;
- 查询参数全部进入缓存键,制造大量碎片并使 Purge 漏掉变体;
- 源站返回旧内容,CDN 正确地缓存了“旧的源站结果”。
第三步:比较边缘与源站
在不绕过 TLS 和 Host 校验的前提下直连源站,带正确 Host 请求同一 URL。若源站已新而边缘旧,问题在 CDN 缓存或刷新传播;若源站也旧,应继续查 Nginx、应用、Redis 和数据库。不要通过公开源站 IP 长期绕过 CDN,这会暴露攻击面。
CDN 与源站的职责可参考CDN 和云服务器的区别。源站 IP 防护和回源规则改变前,应保留上一版配置与访问方式。
静态资源优先用版本化,而不是频繁清缓存
CSS、JS、图片等内容变化时,使用内容哈希或构建版本文件名,例如 app.内容哈希.js。HTML 可较短缓存或重新验证,并引用新文件名。这样旧 HTML 仍能找到旧资源,新 HTML 指向新资源,发布和回滚都更可控。
仅在文件名不变时依赖查询参数,也要确认 CDN 缓存键是否包含该参数。不要把随机时间戳加到每次访问,长期会破坏缓存命中并增加回源。
HTML、API 与登录页面怎样设置
公开且变化不频繁的 HTML 可以设置短 TTL 和重新验证;强时效页要根据业务缩短。含账号、订单、购物车或个性化数据的响应通常不应进入共享缓存,需正确使用 private、no-store 或供应商绕过规则。API 是否缓存取决于方法、认证、数据时效和幂等性,不能用一条全站规则覆盖。
no-cache 的语义是使用前重新验证,不等于“完全不存储”;no-store 才是要求不存储。错误理解会造成一边以为关闭缓存、一边仍看到条件请求。
Purge 怎样做才安全
优先按精确 URL 或标签清理,而不是全站。清理前确认缓存键包含的主机、路径、查询参数、协议和设备维度;否则可能只清掉一个变体。全站 Purge 会在热门站点形成回源洪峰,应提前确认源站容量和限流。
发布失败时,版本化资源可以把 HTML 引用切回上一构建;若已 Purge,应确保旧构建文件仍存在。不要在回滚前删除所有旧静态资源。
一套可重复的验收表
- 源站返回新版本摘要和正确缓存头。
- 至少两个地区/运营商的边缘节点返回新版本。
- HIT/MISS、Age 与预期 TTL 一致。
- 无痕窗口和普通窗口都能按策略更新。
- 登录、购物车、订单等私有页面没有被共享缓存。
- 404、重定向和 API 错误没有被异常长时间缓存。
- 回源请求、带宽和 5xx 未因刷新出现尖峰。
若缓存故障来自源站性能不足,可结合云服务器监控告警指南和服务器 502/504 排查。需要隔离环境复现回源与版本发布时,可查看美国轻量云或美国弹性云服务器的实时配置。
常见问题
清了 CDN 缓存为什么浏览器还是旧内容?
浏览器、Service Worker、源站代理或应用缓存仍可能保存旧版本。用无痕、禁用缓存和响应摘要分层验证。
Age: 0 就是没有缓存吗?
不一定。可能是刚进入共享缓存或被重新验证。还要结合供应商缓存状态头、后续请求和源站日志。
no-cache 和 no-store 一样吗?
不一样。no-cache 允许存储但使用前应重新验证,no-store 要求不存储响应。
全站 Purge 能作为每次发布步骤吗?
不推荐。它会降低命中并增加回源风险。静态文件版本化、精确 URL/标签清理更可控。
CDN 多久会在所有节点更新?
取决于供应商、刷新方式、节点和缓存键。不能只看控制台“成功”,应从目标地区读取响应头和版本摘要验收。