资讯文章 资讯 · NEWS
样式改完了,用户三天后还看到旧版:目录站的静态资源缓存和发布顺序
2026-10-23 约 1,682 字 预计阅读 4 分钟 yiyunkj
样
上周我把分类页的间距调了一遍,自己刷新看到的是新版,第二天有用户在群里贴了张截图,还是旧的。让他强制刷新一下才好。这种反馈一多,你就会开始怀疑是不是改错了,其实问题在缓存。

图片来源:网络,版权归原作者所有,侵权请联系删除
一个 URL 只能有一种缓存策略
浏览器缓存这件事的混乱,多半来自把两种策略用在了不该用的资源上。这两种策略的区别只有一句话:URL 本身就是版本号,还是 URL 固定、内容靠服务端告诉客户端有没有变。
| 资源 | 该给的指令 | 理由 |
|---|---|---|
| HTML 文档 | no-cache + ETag | URL 稳定,内容会变,用之前必须回源确认 |
| 带指纹的 JS / CSS | max-age=31536000, immutable | 内容变了文件名就变,旧文件永远不会改 |
| 无指纹的图片 | max-age=3600 + 重新验证 | 文件名不变,只能靠短新鲜期加 304 兜底 |
最常见的错法是把 HTML 也设成一年。HTML 是那个「告诉浏览器该加载哪个指纹文件」的文档,它一旦被缓存住,后面所有资源更新都传不出去。反过来把所有资源都设成 no-store,每次访问全量回源,首字节时间立刻变差。
文件名指纹是长缓存的前提
很多站用的是 style.css?v=3 这种查询串版本号。能用,但有两个隐患:一部分代理和 CDN 的缓存键会忽略查询串,v=4 和 v=3 会被当成同一个文件;有些下载工具和离线缓存也会把查询串当噪声。结果就是你以为换了版本,实际发出去还是旧内容。
把内容哈希写进文件名里没有这个问题。app.a1b2c3d4.css 的哈希由内容算出来,改一个字节文件名就变,新旧文件可以同时存在。这对滚动发布特别重要:某个用户的 HTML 还是上一版,指向的旧资源文件必须仍然能访问,否则他看到的是白屏或者没样式的裸 HTML。
哈希长度取 8 到 12 位十六进制就够。构建产物里记得输出一份文件名映射表,模板引用时查表,别手写。
immutable 到底在解决什么
这条指令出自 RFC 8246,2017 年 9 月发布。含义是:在这个新鲜期内,这个 URL 的内容保证不变,缓存不需要再来问。
它真正省的是「用户主动刷新」这一下。不带 immutable 时,用户按 Ctrl+R,浏览器会对页面上每个缓存资源发一次条件请求,服务器回一堆 304。这些请求没传内容,但往返延迟是实打实的。Firefox 49 和 Safari 11 支持这条指令,刷新时直接跳过重新验证。Chrome 的情况各家资料说法不一致,官方立场是普通刷新本来就不会重新验证子资源,所以这条指令在 Chrome 上基本是冗余而不是有害。
有一条铁律:没有文件名指纹的资源,绝对不能加 immutable。文件名不变而内容会变的情况下加这条指令,等于告诉浏览器「这一年都别看新的」,用户只能自己清缓存。
发布顺序比缓存配置更容易出错
正确顺序只有一种:先上传新资源文件,再切 HTML,最后清 CDN 上的 HTML 缓存。旧资源文件保留 7 到 30 天,等所有会话都过期了再删。
顺序颠倒的两种后果都很具体。新 HTML 先上线、资源还没传完,用户拿到的是 404 的 CSS,页面成了纯文本;旧 HTML 还在、资源已经被覆盖删除,同样是白屏。中间那段窗口可能只有几十秒,但如果你的用户正好在刷新,他记住的就是「这站坏了」。
用版本目录能省掉这件事。/assets/20261023/ 整目录上传,HTML 里引用新目录,回滚就是把引用改回上一个目录,连重新构建都不用。
上线后怎么确认生效了
三个动作,两分钟能做完。
- 对带指纹的资源执行
curl -I,看 cache-control 里有没有 immutable,max-age 是不是一年。 - 浏览器里刷新页面,DevTools 的网络面板看资源来自 memory cache 或 disk cache,而不是一串 304。
- 看 HTML 响应头的 max-age。超过 300 秒就要小心,改版到用户看见之间的延迟会按这个值放大。
缓存配置不会让排名上升,但配置错了会让你的每一次改版都白做。目录站改版频繁,这一项值得一次配好。
如果站点前面挂着 CDN,还要多一步:确认 CDN 的缓存键包含完整路径,刷新接口能按目录批量清。清缓存这件事靠人记不住,最好接在发布脚本的最后一步。