先给结论:不要从最外层缓存开始清,而要先确定“哪个版本被写进了索引”。做法是固定一个可复现的请求,逐层比对响应头、正文指纹和最后修改时间,找到第一处版本分叉的层级,再决定是保留、改写还是退出该层缓存。旧内容退出时,最外层的 CDN 或反向代理缓存往往最先返回旧版本,但它通常不是根因。
多层缓存常见组合是浏览器缓存、CDN、反向代理(如 Nginx、Varnish)、应用层对象缓存、数据库查询缓存。版本不一致的表现是:同一 URL 在不同网络、不同地区、不同 UA 下返回不同正文,但 HTTP 状态码都是 200。
定位时先构造一个带唯一标记的请求,例如在测试页写入 <meta name="build" content="v-2024-06-a">,然后逐层抓取:
Last-Modified。Age、X-Cache、Via。如果源站与反向代理一致,而 CDN 返回旧版本,分叉点在 CDN;如果反向代理就已经是旧版本,问题在代理层或应用层的缓存键设计。这个动作的结果直接决定下一步:分叉点在上游时,清下游缓存只会短暂掩盖问题。
当缓存键只包含 URL,而页面内容依赖 Cookie、语言、设备或登录状态时,不同用户会互相拿到对方的版本。证据是:同一 URL 在带与不带某 Cookie 时返回不同正文,但响应头里没有 Vary 或 Vary 值不完整。此时应保留缓存,但改写缓存键,把真正影响正文的维度加入键或 Vary。
源站更新后,应用层已失效,但 CDN 仍按旧 TTL 提供内容。证据是 Age 接近该层配置的最大值,而源站 Last-Modified 已更新。此时可保留分层缓存,但要统一失效顺序:先让最靠近源站的层失效,再逐层向外推送,而不是反向操作。
旧内容退出时,常发现某个已不再维护的子系统仍持有自己的缓存副本,例如旧 CMS 的静态化目录、旧合作方镜像站。证据是该版本只在特定路径或特定来源 IP 下出现,且响应头带有非当前架构的标识。这类缓存不适合继续保留,应连同入口一起退出,否则每次更新都要额外同步一次。
三种处理方式都有成立条件,不必全部采用。
一个假设例子:某栏目计划下线,但旧合作方镜像仍在提供旧版本页面。若该栏目内容仍有检索价值,可选择改写——保留镜像但改为指向新地址;若内容已无价值,则退出镜像并让原 URL 返回 410 或 301。判断依据是内容是否还有承接页面,而不是缓存层本身是否“看起来旧”。
请求量归零、抓取量下降或某层缓存命中率变成零,都不能单独证明缓存已一致。合理替代解释包括:抓取工具更换了出口、该层被临时绕过、监控只覆盖了部分节点。应同时比对正文指纹、响应头和不同节点的返回结果。
另外,robots.txt 的抓取限制不等于可靠的索引移除,站点地图也不保证收录。缓存一致性解决的是“返回哪个版本”,与是否被索引是两件事,不要用清除缓存来替代退出流程。HTTPS 同样不保证内容版本一致或安全无漏洞,它只解决传输层问题。
可执行的一步是:为每个缓存层建立一张对照表,记录该层的缓存键、TTL、失效触发方式和负责人。每次发布后按表逐层核对指纹,出现分叉时先查表定位,再决定保留、改写或退出。这样下一次版本不一致时,排查范围会从“全部缓存”缩小到具体一层。