内容改完发布,自己刷新一看还是旧版本;再过一天去看,蜘蛛快照里也还是老样子。遇到这种情况,先别急着怀疑程序写错了,多数时候是缓存没有跟着发布动作一起更新。缓存本身是必要的,问题在于“发布”和“刷新”被拆成了两件事。
缓存通常不止一层
一个页面从服务器到访客屏幕,中间可能经过好几层缓存,改了一层不代表全都生效:
- 浏览器缓存:由 Cache-Control、Expires 控制,访客本机留存的副本,服务器刷新了它也不会自动失效。
- CDN 边缘节点:每个节点各存一份,回源一次后在 TTL 内一直用自己那份。
- 反向代理或页面缓存:Nginx、Varnish 之类的整页缓存,缓存键设计不好时容易串页。
- 应用层缓存:对象缓存、查询缓存、模板编译缓存,改数据库不一定改缓存。
所以“我明明改了”这句话要先落到具体一层上,否则排查会一直在原地打转。
先判断看到的是哪一层的旧内容
命令行请求一次首页或内容页,重点看响应头:
- 多次请求同一地址,观察 Age 是否递增,递增说明命中了中间层缓存。
- 看 X-Cache、CF-Cache-Status、X-Cache-Hits 这类字段,判断是 HIT 还是 MISS。
- 带一个随机查询参数再请求一次,如果内容变新,基本可以确定问题在按完整 URL 缓存的那一层。
- 对比登录态和未登录状态,确认有没有把个性化内容缓存成了公共页面。
浏览器自身还有一层本地缓存,直接刷新未必能绕过。判断之前先开无痕窗口或强制刷新,避免把本地副本当成服务器状态。
把刷新动作写进发布流程
靠人工想起来点一下刷新按钮,迟早会漏。更稳的做法是把步骤固定下来:
- 保存发布后,触发对应范围的刷新:单 URL、栏目目录或按标签批量刷新。
- 等几秒,让各个边缘节点收到失效通知。
- 用命令行请求目标地址,确认返回的是新内容而不是 HIT 状态。
- 检查静态资源版本号是否更新,避免 HTML 是新的、CSS 还是旧的。
- 在发布记录里留一条时间戳,后续出问题能对照。
静态资源建议用带版本或哈希的文件名,HTML 用较短的缓存时间或协商缓存。这样每次发版天然就是新地址,不必依赖全站清理。
缓存键和参数容易踩的坑
有的配置把完整 URL 连同参数一起当缓存键,同一个页面带上不同跟踪参数就各存一份,既占空间又容易内容不一致;反过来,如果配置忽略参数,又可能把两个不同内容的页面缓存成同一份。
做了移动端和桌面端分发的站点,要核对 Vary 头。用 User-Agent 做区分会把缓存打散成很多份,命中率下降;用一套响应式页面则可以省掉这个问题。带登录态、带个性化推荐的区块,不要走整页缓存,或者把它放到页面加载后再取。
有些地方不该设长缓存
- 后台、草稿页、预览页:应当明确不缓存,避免修改后长期看不到变化。
- 带临时令牌的分享链接:缓存后可能被无关访客命中。
- 错误响应:404 和 5xx 不要缓存,否则问题修好了,访客看到的还是错误页。
- robots.txt 和站点地图:建议短缓存,规则调整后能较快生效。
定期抽查,比事后猜更省事
挑出首页、几个主要栏目页和若干内容页,每周固定请求一次,记录缓存命中状态和刷新耗时。如果某次刷新后长时间仍是旧内容,说明失效通知没送达,或者缓存键设计有问题,这时候再去调配置才有依据。
缓存不是配置一次就不用管的东西。把“更新—刷新—验证”当成一个完整动作,能省掉大量“到底是没更新还是没刷新”的来回确认。