记一次愚蠢的缓存危机
问题
今天改了 manifest.json
, 推到服务器上后, 一直不生效。看了一下缓存头, Cache-Control: max-age:31536000, must-revalidate
, 糟糕。。。
原因
- 下列所述 "过期" 是指 stale: 客户端综合计算
age
+max-age
+Date
得出的当前资源是否"新鲜 (fresh)"的状态;- 源服务器综合考虑
If-Modified-Since
或If-None-Match
后, 回复客户端是否过期 (如果已过期, 会在 body 中返回 200 + 新的资源; 如果没过期, 就返回 304);- 下列所述 "不能" / "必须" / "应该" 都是指服务器如此要求, 但是否执行取决于客户端的实现, 你完全可以自己编译一个浏览器, 忽略请求头, 应该不犯法;
no-store
表示不能缓存no-cache
表示可以缓存, 但客户端每次使用都必须与源服务器验证是否有变must-revalidate
表示可以缓存, 但一旦过期, 就不能继续使用该资源, 而是必须与源服务器重新验证才能使用, 如果连不上源服务器, 那你就别用了 (注意, 平时没过期的时候, 客户端是不会去找服务端验证的)stale-while-revalidate
表示即使缓存过期了也还能用, 只要你用了之后重新验证并更新缓存就好了, 但是如果验证出错了 (500 或 404 或其他什么错误), 那你下次就不应该再用那个过期的资源了 (非标准值, 预期浏览器兼容性差)stale-if-error
表示即使缓存过期了也还能用, 之后重新验证并更新缓存就好了, 万一重新验证出错 (500 或 404 或其他什么错误), 你下次还能用旧的缓存 (兼容性差, 约等于没有)immutable
表示只要还没过期,你就尽管用, 不要来问我服务器, 肯定不会变 (兼容性差, 约等于没有)
不应变化的静态资源可以适用 must-revalidate
, 例如不会变化的文件如 vconsole.min.js
, 自带版本号的文件如 sensorsdata-20110513.min.js
, 这类文件无需打包, 可以直接放在 public
静态目录中, 内容长期不会变化, 如果有变, 我们也会修改版本号, 所以可以适用长期缓存, 且客户端每次使用都无需重新验证。(这种情况其实也能用/更适合用 immutable
)
但是像一些可能有变、且不能随便修改文件名的文件, 千万不要使用 must-revalidate
(即使要用, 也设一个短一点的 max-age
)。如 favicon.ico
, manifest.json
等, 应该用 no-cache
, 每次用的时候必须验证是否有变。
解决办法
没什么好办法, 只能庆幸这是一个没人访问的私人站点。。。
-
手动清理浏览器缓存, 发现还是没用, 然后发现
Response Headers
中有另一个 header:X-Cache: HIT
, 所以: -
清理
nginx
缓存
OK, 勉强搞定
后记
最后, 如果真的有用户访问的项目遇到了这样的问题, 我们没办法一个一个去清理用户的浏览器, 那么我们能怎么做呢?
-
Deleting stored responses 说, 可以给服务器发个同 url 的 POST 请求
One of the methods mentioned in the specification is to send a request for the same URL with an unsafe method such as POST, but that is usually difficult to intentionally do for many clients.
-
同样是 1 中的链接说的, 使用 Clear-Site-Data: "cache" 标头 (但是并非所有浏览器都支持该标头, 且该方法只能清空浏览器缓存, 无法处理中间缓存
intermediate caches
)
总结来说, 就是谨慎点, 别搞出这样的事, 给自己找麻烦。