先给结论:不要从“哪个缓存错了”入手,而要先确定你期望哪一个版本成为对外可见版本,再逐层比较同一请求在各层拿到的响应。多层缓存返回不同版本,通常不是收录入口本身的问题,而是各层缓存键、刷新时机和变体协商不一致。定位顺序应是:固定请求条件、记录每层响应、找出第一处分叉,再决定清哪一层、改哪个键,而不是把所有缓存一起清掉。
同一页面在不同设备、不同地区、不同登录状态或不同 Accept-Language 下,本来就可能是不同版本。多层缓存不一致时,第一步不是清缓存,而是把变量压到最少:选定一个 URL、一个 User-Agent、一个不带 Cookie 的请求、一个固定查询参数。用同一组条件分别请求边缘缓存、反向代理缓存、应用层缓存和源站。
判断依据是响应头,而不是页面肉眼观感。重点看 Age、Cache-Control、ETag、Vary、Last-Modified 以及内容长度或正文哈希。如果边缘返回的 Age 很大,而源站返回 Age: 0,说明边缘存的是旧副本;如果两者 ETag 不同但正文相同,可能只是压缩或换行差异,不必当成版本冲突。
这一步的实际动作是给每层响应各存一份原始头和正文摘要。只有拿到这组对照,下一步才能判断分叉发生在哪一层,而不是凭“刷新后好了”下结论。
多层缓存的常见结构是:CDN 边缘 → 反向代理或网关 → 应用内缓存 → 源站。版本不一致时,从最靠近源站的一层往回比,比从外往里猜更有效。因为一旦源站和它上一层就不同,问题在生成或应用缓存;如果源站一致、只有边缘不同,问题在边缘缓存键或刷新。
Vary、地域分片和刷新范围是否只清了部分节点。这里有一个容易误判的现象:请求量或抓取量突然归零,并不能单独证明你的清理动作正确。它也可能来自上游限流、监控口径变化、机器人调度变化,或该 URL 本来就不在活跃抓取队列里。把归零当作唯一证据,会把缓存问题误判成收录问题。
假设你有一个商品页,源站返回 ETag: "v2",反向代理也返回 "v2",但边缘返回 "v1" 且 Age 为 86400。这组数据指向边缘副本未更新,而不是源站没发布新版本。此时应先确认边缘的缓存键和刷新范围,再决定是否回源强制刷新;如果直接改源站,边缘旧副本仍会继续对外提供。
版本不同有两种典型原因,处理方式不同。缓存键问题表现为:同一 URL 因某个请求头、Cookie 或查询参数被拆成多个副本,你只刷新了其中一个。刷新时机问题表现为:键一致,但某一层还没到过期时间,或刷新只作用于部分节点。
区分方法是:用完全相同的一组请求条件连续请求两次,观察各层 Age 和 ETag 是否稳定。如果同一条件下边缘仍返回两个不同 ETag,更可能是键或分片不一致;如果稳定返回旧 ETag,更可能是刷新范围或过期时间问题。
对应的动作也不同:键问题要改缓存键规则或统一变体,刷新只能暂时掩盖;刷新问题要确认刷新是否覆盖全部节点,并检查 Cache-Control 的 s-maxage 与 stale-while-revalidate 是否让旧版本继续被返回。改完键规则后,下一步应重新采集一轮各层响应,确认分叉消失,而不是直接去提交网址收录。
多层缓存版本不一致时,先不要急着提交网址收录。原因很直接:如果对外可见版本本身不稳定,抓取端拿到的内容可能与你预期不同,提交后也可能只是让一个旧版本更快被看到。应先让各层对同一请求返回一致版本,再观察抓取端实际拿到的是哪一版。
站点地图不保证收录,robots.txt 的抓取限制也不等于可靠的索引移除。这两点在这里的意义是:不要把缓存一致性问题和收录控制混在一起处理。缓存未统一前,提交、站点地图或抓取限制都无法替代版本一致这一前提。
可执行顺序是:固定请求条件 → 采集各层响应 → 定位第一处分叉 → 修正缓存键或刷新范围 → 复采确认一致 → 再检查抓取端拿到的版本 → 最后才考虑提交网址收录。每一步的结果决定下一步:如果复采仍分叉,就回到分叉层继续查;如果已一致,才进入收录观察。