site logo

Marico's space

你修复了链接,但它还是失效:缓存的 404 和重定向如何比你的修复活得更久

前端技术 2026-11-12 17:33:59 3

最近折腾了一个“明明已经修好,读者却还是打不开”的链接,踩了几个缓存相关的坑,这篇把问题彻底说清楚:为什么页面恢复了,404 和旧重定向却还活着,以及该从哪一层开始排查。

你收到链接失效的报告,找到原因,恢复页面或者加上重定向,自己测试也正常。结果一个小时后,又有人反馈:还是坏的。

你没看错,对方也没看错。从服务器到读者之间不止一个位置会缓存 HTTP(超文本传输协议)响应,错误响应和成功页面一样会被缓存,重定向也一样 Sometimes 缓存的时间还远超预期。

下面按缓存层级逐个拆开:它为什么会记住错误响应,以及怎么判断到底是哪一层在“拖后腿”。

第 1 层:浏览器缓存

浏览器会按照 HTTP 缓存规则缓存响应,规则定义在 RFC 9111 中。最关键的响应头是 Cache-Control,老一些的方案还会用 Expires。

按照这套规则,404 响应默认可以被缓存。也就是说,即使没有显式设置缓存响应头,缓存层也可能通过启发式规则存储并复用错误响应。实际使用中,浏览器对错误响应通常比较保守,但如果服务端通过一条笼统的配置规则,给包括 404 在内的所有响应都加上了类似下面的响应头:

Cache-Control: public, max-age=86400

那么浏览器完全有理由在一天之内继续显示这个 404。

怎么判断:让对方打开无痕窗口,或者用不带缓存的 curl 测试。如果 curl 能看到修复后的页面,浏览器却看不到,问题就在本机缓存。

第 2 层:被缓存的重定向,最难处理的那个

永久重定向是最容易让人意外的情况。301 和 308 默认都可以被缓存,浏览器通常还会缓存得很激进。

比如之前误把 /pricing 永久重定向到了 /plans,后来又删掉了这条规则。访问过它的浏览器可能仍然会直接跳到 /plans,根本不再向服务器询问 /pricing 的最新状态,而且这个状态可能维持很久。

所以,迁移期间“先临时用一下 301”往往很难收场。只要还不能确定重定向永远不会变,使用 302 或 307 会更容易撤回。

怎么判断:在浏览器开发者工具的网络面板里勾选“Disable cache”,再对比请求结果。如果重定向标记为“from disk cache”或“from memory cache”,基本可以确定是浏览器缓存。

第 3 层:CDN 或反向代理

如果网站放在 CDN(内容分发网络)或缓存代理后面,例如 Varnish、nginx 的 proxy_cache,或者云厂商的边缘网络,这一层可能已经缓存了 404。

很多 CDN 会单独设置错误响应的缓存时间,通常叫“negative caching”或“error caching”。不同厂商、不同套餐的默认值并不一样。有些服务默认只缓存 404 一小段时间,目的是防止大量不存在的 URL 把源站打挂。

这也是“明明修好了,但所有人都还是打不开”最常见的原因:源站已经恢复正常,边缘节点却还在返回之前存下来的错误,直到缓存时间,也就是 TTL(生存时间)过期。

怎么判断:查看响应头。多数 CDN 会暴露缓存状态,常见的有 Age、X-Cache: HIT,也可能是厂商自定义的响应头。如果 Age 大于你修复后经过的时间,就说明看到的是旧副本。

curl -sI https://example.com/docs/install | grep -iE '^(HTTP|age|cache-control|x-cache|cf-cache-status)'
Enter fullscreen mode Exit fullscreen mode

怎么修复:从 CDN 中清理这个 URL,同时检查错误响应缓存配置。

第 4 层:应用自己的缓存

框架和 CMS(内容管理系统)通常都有自己的页面缓存,例如构建时生成的静态 HTML、全页缓存插件、内存中的路由缓存。如果 404 已经在这一层生成并缓存下来,那么修改数据库里的内容并不会立即改变最终响应,必须先重建缓存。

静态站点生成器更特殊:如果页面在构建时不存在,那么无论你把源码修好多少次,在下一次构建和部署之前,它都会继续缺失。

怎么判断:绕过 CDN,直接请求源站,例如使用内部域名或者 curl --resolve,看看源站自身是否仍然返回 404。

第 5 层:DNS,链接指向的域名发生变化时

如果链接失效是因为域名出了问题,比如 docs.example.com 缺少 DNS(域名系统)记录,那么就要考虑 DNS 缓存。

域名解析器会按照记录的 TTL 缓存解析结果,很多解析器还会根据区域配置缓存“这个域名不存在”的负向响应。即使新记录已经添加,也可能要等这些负向缓存过期,所有人才能看到最新结果。

怎么判断:直接查询几个公共 DNS 解析器,对比返回结果。

第 6 层:搜索引擎和链接预览

最后还有一些不受你控制的部分。搜索引擎抓取过损坏的 URL 后,在重新抓取之前,搜索结果里可能继续显示错误,或者直接移除该页面。聊天应用和社交平台也会缓存链接预览,有时缓存时间还很长,因此页面恢复后,分享出去的链接仍然可能显示错误卡片。很多平台都提供了刷新预览的工具。

“还是坏的”排查决策树

  1. 用 curl 请求公开 URL,返回的是修复后的响应吗?
    • 不是 → 问题在服务端:CDN、应用缓存或源站。检查 Age 和缓存响应头,然后直接请求源站。
    • 是 → 问题在客户端或下游链路:浏览器缓存、被缓存的重定向、用户网络中的 DNS,或者缓存的链接预览。
  2. 这是重定向吗?优先怀疑浏览器缓存的 301。
  3. 问题涉及域名吗?优先怀疑 DNS 负向缓存。

提前规避这些问题

  • 不要给错误响应设置过长的 max-age。针对 4xx 和 5xx 响应,显式设置较短或不允许缓存。
  • 重定向没确定之前,先用临时状态码。确认永远不会被改回后,再切换到 301 或 308。
  • 了解 CDN 的错误缓存配置。同时要清楚如何快速清理单个 URL。
  • 按照读者看到的方式检查链接。测试公开 URL,也就是经过完整公网链路的地址,不要只测源站或源码文件。

最后这个习惯正是 linkbruno.com 在做的事:它从外部检查页面中的链接,走一遍读者访问时经过的所有缓存链路。

延伸阅读

  • 你的链接检查器应该遵守 robots.txt 吗?来认真讨论一下
  • 复盘:部署删掉了上周的 JavaScript 包,缓存页面却还在请求它们
  • 重定向文件应该只追加,删除重定向本身就是破坏性变更
  • 重定向链:为什么 3 跳已经多了 1 跳,以及一个发现重定向链的脚本

你遇到过的链接故障里,哪一层坚持得最久:浏览器、CDN,还是什么更离谱的东西?