站点运营

站点运营:静态资源缓存与版本号自查,别让访客继续加载旧文件

改版上线后仍然有访客看到旧样式、旧图片,多半不是代码没发上去,而是缓存没更新。这篇文章按浏览器、CDN、反向代理、源站的顺序梳理缓存排查思路,说明 Cache-Control、ETag、Expires 几个字段该怎么看,版本号加在哪里才算数,并给出一份可执行的自查清单。

站点运营

站点运营:静态资源缓存与版本号自查,别让访客继续加载旧文件

很多人遇到过同一个现象:前端明明改完了,图片也换了,自己在浏览器里清一下缓存就一切正常,可客服还是会隔三差五收到「样式错乱」「图片还是旧的」这类反馈。多数时候并不是文件没发上去,而是浏览器或 CDN 还在用本地缓存里的旧副本。缓存本身是好事,它省带宽、也让二次访问更快;麻烦在于文件更新了,缓存并不知道。

先分清缓存发生在哪一层

从访客到源站,中间可能叠了好几层:浏览器缓存、CDN 边缘节点、反向代理(Nginx、Varnish 之类),最后才轮到应用本身。排查时按「由近到远」的顺序看,比一上来就重启服务器有效得多。

  • 浏览器:开发者工具的网络面板看 Size 一列,出现 memory cache 或 disk cache 就说明命中了本地;
  • CDN:看响应头里的 X-Cache、Age、CF-Cache-Status 之类的字段;
  • 反向代理:看代理层的缓存目录与访问日志;
  • 源站:以上都没命中,请求才会真正打到后端。

把「这次请求停在哪一层」先确定下来,后面的动作才有针对性。

缓存头里真正需要关心的字段

Cache-Control

优先级最高的控制项。max-age 决定文件在缓存里能活多久;no-cache 并不是不缓存,而是每次使用前都要回源校验;no-store 才是彻底不落盘。对已经带版本号或指纹的静态资源,可以放心设长一些;对 HTML 入口文件,通常设短时间或每次校验,免得入口被卡在旧版本上。

ETag 与 Last-Modified

这两个是给回源校验用的。文件没变就返回 304,省下传输体积。需要留意一种情况:如果同一文件在多台机器上算出的 ETag 不一致(比如集群里拿 inode 当依据),校验会反复失败,反而拖慢。遇到这种表现,可以统一 ETag 的生成规则,或者干脆只依赖 Last-Modified。

Expires

属于较早的写法。它和 Cache-Control 同时存在时容易互相打架,建议只保留一处,并且表意一致,别一边写缓存一年、一边写不缓存。

版本号加在哪里才算数

常见做法是给文件名或查询串加指纹,例如 app.3f9a2c.js 或 app.js?v=3f9a2c。几个要点值得记一下:

  1. 文件内容没变就不要动版本号,否则每次发布都让全部访客重新下载一遍;
  2. 入口 HTML 里引用的资源路径要一起改,别只换了资源却漏了引用;
  3. 尽量别拿发布日期或时间戳当版本号,除非你确实每次都有真实变更;
  4. CDN 或对象存储上的旧文件先别急着删,留一段时间给还没刷新的访客。

一份可以直接照做的自查清单

  • 抽查首页、栏目页、详情页的 HTML 响应头,确认缓存时间符合预期;
  • 抽查 JS、CSS、图片的响应头,确认是否带长缓存和指纹;
  • 检查有没有同名文件被覆盖、版本号却没变的情况;
  • 确认 CDN 刷新策略:是全量刷新还是按目录刷新,谁有操作权限;
  • 确认回源时缓存头是否被正确透传,有的代理会顺手改写;
  • 完整记录一次「改文件—发布—验证」,看看各层分别花了多久才更新。

发布节奏上的两个小习惯

一是把 HTML 和静态资源分开对待:HTML 走短缓存,资源走长缓存,这样内容更新能马上生效,资源又不至于反复下载。二是动手前先问一句「这次改的是内容还是文件本身」,如果只是文案微调,通常不需要动版本号,改了反而增加无谓的流量。

缓存问题的排查顺序永远是:先弄清请求命中了哪一层,再决定清哪一层。盲目清缓存只能解决一时,下次还会遇到。

最后提醒一句,缓存策略不是设一次就完事。换 CDN、调服务器、换构建工具,都可能悄悄改变响应头。把它固定成上线检查里的一个环节,以后能省下不少解释成本。