先给结论:当浏览器、CDN 和源站对同一个 404 页面返回不同版本时,不要从“谁的缓存坏了”入手,而要先固定一个可复现的请求,把每一层实际返回的状态码、响应头和正文特征分别记下来,再判断差异属于缓存键设计、缓存过期策略还是源站输出本身不一致。下面以一个你已经能稳定复现的 404 地址为对象,给出逐步处理方案。
直觉上你会认为同一个 URL 在不同网络下看到不同内容,是缓存没刷新。但更常见的情况是:你看到的根本不是同一个请求。浏览器可能带上了 Cookie、Accept-Language、User-Agent,而 CDN 的缓存键只包含路径,于是不同客户端命中了不同缓存对象。
可执行动作:用一个固定请求做对照。至少记录四项——HTTP 状态码、Cache-Control、Age、ETag 或 Last-Modified,再加上正文里的一个稳定特征(比如某个标题文本或一段固定文案)。
结果如何影响下一步:如果状态码本身就不一致(有的返回 404,有的返回 200),问题在源站或回源规则,不在缓存刷新;如果状态码一致但正文特征不同,才进入缓存键和版本比对。
多层缓存返回不同版本,通常落在下面三类原因里,它们成立的证据不一样:
Vary 指定的头(如 Accept-Encoding、Accept-Language)输出不同正文。证据是同一路径在不同请求头下命中不同缓存对象,Age 各自独立增长。Age 会从较大值跳回较小值,且正文特征随之切换。这三种解释不能靠“清一次缓存看看”区分,因为清缓存会同时改变三者的表象。先固定请求,再逐层比对,才能把原因收敛到一类。
把请求路径拆成三段:客户端到 CDN、CDN 到源站、源站自身。对每一段单独取样,而不是一次性看最终结果。
Age 和正文特征。ETag 或正文指纹,判断 CDN 返回的是否就是源站当前版本。假设一个短例子:源站对 404 页面输出带时间戳的正文,CDN 缓存 10 分钟。你上午看到版本 A,下午看到版本 B,直觉是“缓存没同步”。但绕过 CDN 连续请求源站,如果正文每次都在变,那问题在源站输出不稳定,缓存只是把这种不稳定放大了。这个例子里的数字仅用于说明比较方法,不代表任何真实站点配置。
定位到层级后,修改方向完全不同:
动作与结果的衔接:改完后不要只看一次结果,而是用同一固定请求在多个时间点重复取样,确认 Age 与正文特征的对应关系稳定。如果仍有跳变,说明还有一层没纳入比对,回到第三步继续剥离。
请求量、抓取量或某个统计突然归零,不能单独证明你的处理正确。它也可能来自采样窗口变化、日志延迟、监控口径调整,或该路径本来访问就少。同理,robots.txt 的抓取限制不等于可靠的索引移除,站点地图也不保证收录,这些与缓存一致性是不同层面的问题,不要混在一次判断里。
对 404 页面设计而言,一致性问题的核心不是“哪个缓存坏了”,而是同一个请求在各层是否被当成同一个对象处理。先固定请求、再逐层取样、最后按证据归因,这套顺序能让你在不猜的前提下决定改缓存键、改过期策略,还是先改源站输出。