浏览器缓存

一、缓存位置(按优先级从高到低)

位置特点生命周期
Service Worker Cache由SW接管,支持离线持久化,需手动更新
Memory Cache内存缓存,读取极快关闭标签页后释放
Disk Cache硬盘缓存,容量大持久化,按缓存规则过期
Push CacheHTTP/2推送缓存会话结束即释放

DevTools 中看到 from memory cache/from disk cache 仅表示命中位置,不代表缓存策略。


二、强缓存 vs 协商缓存

对比项强缓存协商缓存
是否发请求不发请求,直接用本地缓存发请求到服务器验证
核心响应头Cache-Control(优先)、ExpiresETag/If-None-Match(优先)、Last-Modified/If-Modified-Since
命中状态码200 (from cache)304 Not Modified
适用场景带hash的静态资源(JS/CSS/图片)HTML、频繁变动的资源

1. 强缓存字段

  • Cache-Control(HTTP/1.1,优先)
    • max-age=秒数:缓存有效时长
    • no-cache:可缓存,但使用前必须校验(不是不缓存!)
    • no-store:完全不缓存(真正的禁止缓存)
    • public:允许代理服务器缓存
    • private:仅客户端可缓存
  • Expires(HTTP/1.0):绝对时间字符串,依赖客户端时间,精度低,逐渐被Cache-Control替代。

2. 协商缓存字段

ETag / If-None-Match(优先)

基于资源内容哈希判断变化,精度更高:

  1. 首次请求:服务器返回 ETag: "资源内容哈希值"
  2. 下次请求:浏览器自动携带 If-None-Match: "上次的ETag值"
  3. 服务器对比哈希:
    • 一致 → 返回 304,浏览器继续使用本地缓存
    • 不一致 → 返回 200 + 新资源和新的ETag

Last-Modified / If-Modified-Since(旧方案)

基于资源最后修改时间判断,HTTP/1.0 遗留方案:

  1. 首次请求:服务器返回 Last-Modified: "Tue, 18 Mar 2026 10:00:00 GMT"
  2. 下次请求:浏览器自动携带 If-Modified-Since: "上次的Last-Modified值"
  3. 服务器对比修改时间:
    • 时间一致 → 返回 304
    • 时间不一致 → 返回 200 + 新资源和新的Last-Modified
  • 缺点:仅精确到秒,内容没变但修改时间变了会误判;内容变了但时间没变会漏判。

三、完整缓存流程

  1. 浏览器先检查强缓存是否命中
    • 命中:直接使用本地缓存,返回200 (from cache),不发请求
  2. 强缓存未命中:发送请求,携带协商缓存字段(If-None-Match/If-Modified-Since
  3. 服务器验证资源是否变更
    • 未变更:返回304,浏览器继续使用本地缓存
    • 已变更:返回200 + 新资源,浏览器更新缓存

四、刷新操作对缓存的影响

操作强缓存协商缓存
地址栏回车/点击链接✅ 生效✅ 生效
F5/点击刷新按钮❌ 失效✅ 生效
Ctrl+F5(强制刷新)❌ 失效❌ 失效

五、常见面试题汇总

  1. no-cacheno-store的区别?
    • no-cache:缓存但必须校验;no-store:完全不缓存。
  2. 为什么带hash的资源适合强缓存?
    • 内容变则hash变,文件名变浏览器会重新请求,无需担心缓存旧资源。
  3. 304状态码有响应体吗?
    • 没有,304仅返回头信息,响应体为空。
  4. ETagLast-Modified的区别?
    • ETag基于内容哈希,精度高;Last-Modified基于修改时间,仅到秒,准确度低。
  5. 强缓存和协商缓存哪个性能更好?
    • 强缓存,连请求都不发;304仍需一次网络往返。
  6. HTML为什么不做长期强缓存?
    • HTML是入口文件,引用最新JS/CSS,长期缓存会导致用户一直拿旧版本。

六、Nginx配置要点

通用静态项目配置

server {
    # HTML:协商缓存,每次校验
    location = /index.html {
        add_header Cache-Control "no-cache";
    }

    # 带hash静态资源:长期强缓存
    location ~* \.(js|css|png|jpg|webp|woff2?)$ {
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # SPA兜底
    location / {
        try_files $uri $uri/ /index.html;
    }
}

⚠️ 注意:proxy_cache是Nginx到上游的代理缓存,和浏览器缓存是两回事。