先给结论:当同一网址在不同缓存层返回的 HTML 不一致时,不要从“哪个版本是对的”开始争论,而要先固定一个可复现的请求路径,把每层缓存的响应头、正文指纹和缓存键记录下来,找到第一个出现分叉的层,再决定是清缓存、改缓存键还是改回源逻辑。下面用一个假设情境把决策过程走一遍。
假设一个站点把商品页放在 CDN 后面,CDN 前还有一层反向代理,源站又用了页面缓存插件。运维分别从办公网、移动网络和机房直连请求同一个 URL,得到三种结果:A 版本含完整价格与库存,B 版本价格区为空,C 版本是两小时前的旧价格。三者 HTTP 状态都是 200,Content-Length 却不同。
这种“都成功、内容不同”的状态最容易误导排查方向。它通常不是抓取或索引规则本身出了问题,而是多层缓存对同一个 URL 使用了不同的缓存键,或者某一层在回源失败时返回了过期副本。先别改 robots.txt,也别急着提交站点地图,那两步都绕过了真正的分叉点。
对每条路径,记录四样东西:响应中的 Age、Cache-Control、Vary 和 X-Cache 一类自定义命中标识;正文去掉空白后的哈希;请求携带的 Cookie、Accept-Encoding、User-Agent;以及请求发出的时间。把哈希不同的响应两两对比,先看差异集中在价格、库存、推荐位还是整段模板。
差异位置能缩小怀疑范围。如果只有价格区不同,问题更可能出在片段缓存或个性化逻辑;如果整页结构都不同,更可能是不同数据中心或不同缓存版本。动作上,先复制出哈希不同的两份正文存为文件,后续每改一次配置就重新取一次哈希,用哈希是否收敛来判断改动是否生效,而不是凭肉眼刷新页面。
多层缓存各自计算缓存键,常见的输入包括主机名、路径、查询串、Vary 指定的请求头,以及 CDN 或代理自定义的规则。逐层检查时,可以临时构造只差一个变量的请求:同一路径分别带与不带某个查询参数,同一路径分别带与不带登录 Cookie,观察哪一层开始返回不同版本。
这一步的产出应该是一句话:分叉发生在第几层、由哪个缓存键字段触发。没有这句话,后面的清理动作都是碰运气。
两者表现相似,处理方式相反。过期副本是缓存正常但内容旧,特征是 Age 较大、命中标识显示 HIT,清掉该键后能取到新版本。回源失败兜底是源站不可达或超时,缓存层主动返回了旧副本,特征通常是响应头里出现表示 stale 或 revalidated 的标记,且同一时刻直连源站可能直接报错。
验证方法是:在怀疑的那一层临时绕过缓存直连源站,同时请求一个明确不存在的路径。如果直连源站正常而缓存路径仍旧,说明问题在缓存层;如果直连源站也异常,说明要先修源站可用性,清缓存只是暂时掩盖症状。这个动作的结果直接决定下一步:源站问题优先修源站,缓存问题才进入清理和改键。
三种处置对应不同条件,不能混用。
Vary,否则清完还会再次分叉。每次处置后都回到第一步重取哈希。只有当多条路径的哈希一致、且连续两次请求之间不再跳变,才认为一致性恢复。此时再去检查抓取与索引层面的表现,才有意义;否则你看到的“不收录”可能只是抓取工具恰好取到了那个残缺版本。
假设情境的收尾是这样:排查发现反向代理把 Accept-Encoding 纳入了缓存键,但 CDN 没有,于是同一 URL 在压缩与非压缩请求下命中了不同副本,其中一个副本是发布中断时的半成品。处置是把该维度在两层缓存键中统一,并清掉受影响键。之后用同一组请求头重复请求五次,哈希全部一致,再去观察抓取日志中该 URL 返回的正文是否稳定。
需要提醒的是,抓取量下降或某路径返回 200 并不单独证明问题已解决,也可能是抓取预算变化、发布节奏或路由调整造成的。判断一致性是否恢复,依据应是同一请求条件下正文指纹稳定,而不是某一个统计数字归零。若站点还用 robots.txt 限制过抓取,要清楚那只是抓取层面的限制,不等于内容已从索引移除;站点地图提交同样不保证收录,这些都不能替代对缓存一致性的验证。