浏览器缓存机制详解:强缓存与协商缓存

默认分类
文章发布日期
2025-09-17
热度
7℃
本文共计
3952字
预计阅读
20分钟

前言:为什么缓存如此重要?
在 Web 开发中,缓存是提升性能、减少服务器压力、加快页面加载速度的核心手段之一。浏览器缓存机制分为两大类:
强缓存(本地缓存)
弱缓存(协商缓存)
很多人只知道“缓存”这个词,却不知道它们背后的机制、HTTP 响应头的作用、以及如何在实际项目中配置。本文将从零开始,带你彻底搞懂这两类缓存。

一、缓存的基本流程图(浏览器视角)

%% 浏览器视角的缓存总览 flowchart TD A[用户访问页面] --> B{本地有缓存?} B -->|是| C{强缓存生效?} B -->|否| D[直接请求服务器] C -->|是| E[200 from cache<br>直接使用] C -->|否| F[发起协商缓存请求] F --> G{服务器验证} G -->|304 无更新| H[沿用旧缓存] G -->|200 有更新| I[下载新资源并缓存]

一、为什么缓存是性能优化的“最后一公里”


  1. 减少重复网络请求,降低 RTT(Round-Trip Time)。
  2. 减轻源站压力,节省 CDN 流量费用。
  3. 提升首屏与交互响应速度,改善 Core Web Vitals 指标。

二、强缓存:浏览器“独断专行”


2.1 定义

在缓存有效期内,浏览器不会发任何 HTTP 请求,直接读取本地副本。

2.2 控制字段

字段 协议版本 示例值 优先级
Cache-Control HTTP/1.1 max-age=3600
Expires HTTP/1.0 Wed, 21 Oct 2025 07:28:00 GMT 低(仅作兼容)

生产环境请只使用 Cache-Control,放弃 Expires。

2.3 Cache-Control 常用指令

  • max-age=<秒>:相对时间,最大有效时长。
  • s-maxage=<秒>:覆盖 max-age,仅对共享缓存(CDN)生效。
  • public:允许任意中间节点缓存。
  • private:只允许浏览器缓存。
  • no-cache强制走协商缓存,每次必须验证。
  • no-store:禁止任何缓存(最严格)。
  • immutable:声明资源永久不变,Firefox/Chromium 均支持。

2.4 强缓存生效表现

http 复制代码
HTTP/1.1 200 OK
Content-Type: image/webp
Cache-Control: public, max-age=86400

DevTools 看到:
200 (from disk cache)200 (from memory cache)

2.5 最佳实践

  1. 静态资源(JS/CSS/字体/图片)使用强缓存 + 文件名 hash
  2. HTML 文件不启用强缓存,以便及时更新入口。

三、协商缓存:浏览器“礼貌询问”


3.1 定义

强缓存失效后,浏览器携带凭证询问服务器资源是否更新;若未变,服务器返回 304 Not Modified,浏览器继续用本地副本。

3.2 两组验证字段

%% 协商缓存交互 sequenceDiagram participant B as Browser participant S as Server B->>S: GET /style.css<br>If-None-Match: "5d8c72a5" S->>S: 计算最新 ETag alt 资源无变化 S-->>B: 304 Not Modified else 资源已变化 S-->>B: 200 OK + 新资源 + 新ETag end
响应头 请求头 说明
ETag If-None-Match 内容指纹(推荐)
Last-Modified If-Modified-Since 最后修改时间(秒级精度)

ETag 优先级高于 Last-Modified;两者可同时存在。

3.3 协商缓存生效表现

首次请求:

http 复制代码
HTTP/1.1 200 OK
ETag: "5d8c72a5"
Content-Type: text/css

再次请求:

http 复制代码
GET /style.css
If-None-Match: "5d8c72a5"

服务器返回:

http 复制代码
HTTP/1.1 304 Not Modified

DevTools 看到:体积 0 B,状态 304。


四、缓存决策树(一张图记住)


%% 缓存决策树 flowchart TD Start([浏览器请求资源]) --> A{Cache-Control<br>允许缓存?} A -->|no-store| B[直接请求<br>不缓存] A -->|public/private| C{是否过期?<br>max-age/Expires} C -->|未过期| D[200 from cache<br>强缓存] C -->|已过期| E{ETag/Last-Modified<br>是否存在?} E -->|是| F[带凭证请求<br>协商缓存] E -->|否| G[重新下载<br>200 OK] F --> H{服务器验证} H -->|304| I[沿用旧资源] H -->|200| J[下载并更新缓存]

五、Nginx 配置模板(可直接用)


5.1 静态资源:一年强缓存

nginx 复制代码
location ~* \.(?:js|css|png|jpe?g|gif|svg|ico|woff2?)$ {
    add_header Cache-Control "public, max-age=31536000, immutable";
    etag on;      # 协商缓存备用
}

5.2 HTML:禁止强缓存

nginx 复制代码
location / {
    add_header Cache-Control "no-cache, must-revalidate";
    etag on;
}

六、验证缓存是否生效


  1. DevTools Network 面板
    • 200 (from disk cache) → 强缓存
    • 304 Not Modified → 协商缓存
  2. curl 快速测试
    bash 复制代码
    curl -I -H "If-None-Match: \"5d8c72a5\"" https://example.com/app.js
    返回 304 即成功。

七、常见误区澄清


误区 真相
F5 刷新会跳过所有缓存 仅跳过强缓存,仍可能 304
no-cache 意思是不缓存 实际强制协商缓存
Ctrl+F5 完全不用缓存 正确,请求头带 Cache-Control: no-cache + Pragma: no-cache
ETag 一定比 Last-Modified 慢 现代服务器哈希优化后差距极小,建议优先 ETag

八、总结速查表


维度 强缓存 协商缓存
是否发请求
响应码 200 from cache 304 Not Modified
控制头 Cache-Control/Expires ETag/Last-Modified
速度 最快 略慢
场景 静态资源 HTML、可变化资源

九、下一步


  1. 审计现有站点:打开 DevTools,按文内决策树逐条比对。
  2. 给静态资源加 hash,并在 CI 中自动注入带 hash 的文件名。
  3. 在 CDN 控制台开启“智能压缩 + 强制跳转 HTTPS”,配合缓存头进一步提速。

把这篇长文加入浏览器书签,下次配置缓存不再翻十篇博客。

评论 (0)

写下你的评论...
0 / 500

暂无评论,快来抢沙发吧!