缓存配错了,要么用户看到旧页面,要么服务器白白多扛几倍请求。两种缓存的职责要分清楚。

强缓存:不发请求

响应头里的 Cache-Control 说了算,命中时浏览器直接用本地副本,Network 面板显示 200 (from disk cache)。

BASH
Cache-Control: public, max-age=31536000, immutable

immutable 告诉浏览器:即便用户点了刷新也别来问我。只有文件名带哈希的资源才敢这么写。

协商缓存:发请求,可能返回 304

强缓存过期后,浏览器带着标识去问服务器。

  • ETag / If-None-Match:内容指纹,优先级更高,能解决同一秒内修改的问题
  • Last-Modified / If-Modified-Since:秒级精度,够用且开销小
JS
res.setHeader('ETag', '"' + crypto.createHash('sha1').update(body).digest('hex').slice(0, 16) + '"');
if (req.headers['if-none-match'] === etag) { res.writeHead(304); return res.end(); }

一套实用的组合

  1. HTML:no-cache,每次都要问服务器,保证发版即时生效
  2. 带哈希的静态资源:max-age=31536000, immutable
  3. 接口数据:no-store,或者按业务给几十秒的短 max-age
关键点是 HTML 必须不缓存。只要 HTML 拿到的是新版,里面引用的资源哈希就一定是新的。

容易忽略的两件事

  • 304 依然要一次完整的网络往返,移动端弱网下首屏照样慢,别把 no-cache 用在资源上
  • CDN 和 Nginx 会重写缓存头,本地验证通过后还要再看一遍线上响应头