Skip to content

浏览器 HTTP 缓存:强缓存、协商缓存与「为什么改了代码用户还看到旧的」

知识背景

静态资源(JS/CSS/图片)和接口响应在到达页面之前,往往会经过浏览器缓存。缓存能减少重复下载、加快二次访问,但也带来经典问题:发版后用户仍加载旧文件
在 Node 或 Nginx 上配置 Cache-ControlETag 等头,会直接影响浏览器行为。前端工程里的「文件名带 hash」「HTML 不长期强缓存」等策略,都是为了和 HTTP 缓存规则打配合。


知识详解与通俗解释

1. 缓存大致存在哪儿?

  • 内存缓存(Memory Cache):当前标签页会话内较快,刷新可能还在;容量小。
  • 磁盘缓存(Disk Cache):落盘,跨会话;容量大。
  • Service Worker 缓存:由 PWA/离线策略控制,优先级与规则更复杂,本文不展开。

可以粗记:浏览器会尽量「少联网、多复用」;是否复用由响应头 + 浏览器启发式共同决定。

2. 强缓存:在「过期前」不问服务器

关键响应头:

  • Cache-Control(现代首选):如 max-age=31536000 表示一年内直接用本地副本,不再发验证请求(在 max-age 有效期内)。
    • no-store:完全不缓存。
    • no-cache:容易误解——实际常配合协商用,表示使用缓存前必须先向服务器验证(见下)。
    • private / public:是否允许共享缓存(如 CDN)缓存。
  • Expires:旧式绝对过期时间;与 Cache-Control 同时存在时,通常 Cache-Control 为准

通俗理解:强缓存像「外卖放冰箱,保质期内不重新点单」;no-store 是「不存冰箱,每次都新买」。

3. 协商缓存:过期或 no-cache 时,问服务器「还能不能用」

浏览器会带条件头,服务器返回 304 Not Modified 则继续用磁盘/内存里的 body,节省带宽。

常见机制:

  • Last-Modified / If-Modified-Since:按文件修改时间。
  • ETag / If-None-Match:按实体标签(常为内容哈希),更可靠(时间精度、多机部署等问题更少)。

通俗理解:协商缓存像「问问店家这份外卖还能不能吃,店家说能,就继续吃旧的;不能说就换新的」。

4. 与前端工程实践的关系

  • 带 hash 的文件名(如 app.a1b2c3.js):内容变则 URL 变,天然绕过旧强缓存。
  • 入口 HTML:通常不宜设超长 max-age,否则用户长期拿不到新引用。
  • 接口 GET:是否缓存要看业务;API 常配 no-store 或短 max-age,避免脏读。

5. Node /静态服务里你在配什么?

express.static、Koa、或 Nginx expires 时,本质都是在写这些头。线上若「静态资源 304 过多」或「从不304」,都值得对照 Cache-ControlETag 配置查一遍。


总结

  • 强缓存Cache-Control/Expires:未过期则可能不发网络请求(或仅从缓存取)。
  • 协商缓存ETag/Last-Modified省流量但仍有往返,适合可复用但可能更新的资源。
  • 发版与缓存:靠「不可变资源 + hash」与「HTML 短缓存或不缓存」组合,比让用户清缓存更靠谱。