先给有条件的结论:如果同一个 URL 在 CDN、反向代理、应用缓存和浏览器缓存中返回的版本不一致,优先怀疑“缓存键没有覆盖某个会改变输出的输入”,而不是先怀疑搜索引擎或域名权重本身。这个结论成立的前提是源站本身对同一输入稳定输出同一版本;一旦源站输出带随机性、时间戳或会话状态,下面的定位方法就会失效,因为不一致来自源站而非缓存层。
多层缓存最容易制造“看起来不一致”的假象。同一个 URL 在带 Cookie 与不带 Cookie 的请求下返回不同内容,是设计如此,不是故障。定位前先用同一组请求条件对比:相同 URL、相同查询串、相同请求头中的 Accept-Encoding 和 User-Agent、相同 Cookie 状态。如果这些条件变了,版本不同属于预期行为。
一个可区分的证据是响应头。记录每层的 Age、Cache-Control、ETag、Vary 和 X-Cache 之类标记,对比同一请求经过各层后这些值是否一致。若某一层的 Age 明显偏大而内容偏旧,说明它命中了更早的缓存副本,而不是源站刚输出了旧内容。
当各层配置看起来都正确、常规清理也已执行,仍然出现版本分裂,通常漏掉的是缓存键的构成。多层缓存各自按自己的键缓存,键里少一个维度,就会把本该区分的两个版本合并成同一个条目。
Vary 声明的头区分,比如按语言、编码或设备类型。验证方式很直接:构造两个只在一个维度上不同的请求,观察各层是否返回不同缓存条目。如果两层给出相同条目而源站对这两个请求本应输出不同内容,就找到了缺口。修复后需要让受影响的缓存条目失效,否则旧条目仍会在 TTL 内继续被命中。
逐层剥离比一次性清空所有缓存更能定位问题。按请求路径从外到内逐跳验证:先只请求最外层,再绕过它请求下一层,直到直达源站。每一步记录返回内容的哈希或关键字段,而不是凭肉眼比较整页。
这个动作的结果会决定下一步:如果问题出在某一层,只需修这一层的键或失效规则;如果每一层单独看都正确、组合起来才不一致,说明各层的 TTL 与失效传播顺序存在竞争,需要统一失效触发点。假设源站更新后只通知了最内层,而外层仍持有旧副本,就会出现“内新外旧”的组合,这与缓存键问题表现相似但根因不同。
上面整套方法有一个明确的失效条件:源站输出本身不确定。如果页面包含每次请求都变化的随机数、实时时间、A/B 分流标记或未固定的会话内容,那么各层返回不同版本可能完全正常,此时继续按缓存键排查只会浪费时间。判断办法是连续多次直接请求源站,看输出是否稳定;源站不稳定,就先固定源站输出,再谈缓存一致性。
另一个容易误判的现象是抓取量或请求量突然归零。它可能来自缓存命中率上升、抓取预算调整、外部链接变化或监控口径改动,不能单独作为“缓存处理正确”的证据。要结合源站日志与各层命中统计一起看。
先固定源站输出并记录基准版本,再按逐层剥离法找到第一处偏离,检查该层缓存键是否覆盖了所有会改变输出的输入。修复并定向失效受影响的条目后,用同一组请求条件复测,确认各层与基准一致,并把这次用到的请求条件与响应头字段固化成可复用的对比脚本,供下次版本分裂时直接调用。