很多站点都遇到过这种情况:编辑在后台发布了新版本,自己刷新能看到,但访客、同事甚至搜索引擎抓到的仍然是旧页面。排查半天发现代码没问题,问题出在缓存——更新确实写进了源站,但某一层缓存还捧着旧副本。
先数清楚链路上有几层缓存
缓存不止一层,出问题时容易只盯着其中一层。一条典型的访问链路里,可能同时存在下面这些缓存:
- 浏览器本地缓存,受 Cache-Control、Expires 等响应头控制;
- CDN 边缘节点缓存,按 URL、目录或文件后缀命中规则;
- 源站前面的反向代理缓存,如 Nginx proxy_cache、Varnish;
- 应用层的页面缓存或整页静态化;
- 对象存储、图床自带的缓存与版本控制。
建议把这条链路画在一张纸上,标注每层的过期时间、清除方式和负责人。很多时候“刷新了没生效”,只是因为刷新的不是真正命中的那一层。
HTML 和静态资源要分开对待
把 HTML 和 JS、CSS、图片用同一套缓存策略,是最常见的失误。两者的更新频率完全不同:
- 带哈希或版本号的静态资源:内容变了文件名就变,可以放心设置较长的缓存时间,减少重复请求;
- HTML 文档:更新频繁,缓存时间要短,或者干脆不缓存,也可以考虑 stale-while-revalidate 这类先返回旧版、后台再更新的策略;
- 接口与动态数据:按业务容忍度单独设定,不要跟着静态资源一起走长缓存。
如果 HTML 被设成了几天甚至更长的强缓存,那么无论后台怎么更新,访客和爬虫看到的都会是上一版内容。
发布之后要做的几步
把下面这些动作写进发布流程,比事后排查省事得多:
- 发布完成后,主动刷新对应的 CDN 缓存,按 URL 刷新还是按目录刷新,取决于改动范围;
- 确认刷新请求调用成功,而不是只点了一下按钮;
- 用命令行工具查看响应头,关注缓存命中标识、Age、Cache-Control 等字段;
- 换个网络或地区再访问一次,确认不是只有本地节点更新了;
- 把刷新时间、刷新范围记在更新日志里,方便和抓取记录对照。
别让缓存掩盖真实的故障
缓存能扛住流量,也能藏住问题。源站返回的错误页如果被缓存下来,影响会一直持续到过期为止。
几类需要特别留意的场景:源站短暂返回 5xx 被边缘节点缓存,用户长时间看到报错;回源失败后的默认页被当成正常内容;缓存键设置过粗,把不同参数、不同登录状态的页面混成同一份。可以给错误状态码设置很短的缓存时间,或者明确不缓存非 200 响应。
缓存对抓取的影响
搜索引擎抓取时同样会经过缓存层。返回旧内容一般不会直接引发严重后果,但可能让内容更新被感知得更晚,尤其在更新频率较高的栏目里。另外要注意响应头中的 Vary 设置,如果按 User-Agent 或编码区分缓存,配置不当会让同一地址出现多种返回结果,增加判断成本。
一个简单的自查节奏
- 内容更新当天:刷新缓存并抽查页面;
- 每周:看一次缓存命中率与回源量,异常波动往往对应配置问题;
- 每次调整缓存规则:先在测试域名验证,再全量生效;
- 每季度:复核一遍缓存时长,把已经不需要的规则清理掉。
缓存策略没有一劳永逸的答案,站点规模、更新频率和服务器配置都会影响选择。关键是把每一层写在文档里、把刷新动作放进流程里,让“更新完成”这件事在用户和爬虫那一侧也真正成立。