HTTP缓存策略之协商缓存与强缓存详解
字数 1711 2025-12-05 16:53:51
HTTP缓存策略之协商缓存与强缓存详解
一、题目描述
HTTP缓存是Web性能优化的核心技术之一,能显著减少网络传输、降低服务器负载。HTTP缓存分为强缓存和协商缓存两种机制:
- 强缓存:浏览器不向服务器发送请求,直接使用本地缓存
- 协商缓存:浏览器向服务器验证缓存是否过期,决定是否使用缓存
理解这两种缓存的工作流程、相关HTTP头部以及适用场景,是构建高效Web应用的基础。
二、知识背景
HTTP缓存基于HTTP协议中的缓存控制头部实现,主要涉及:
- 强缓存:
Cache-Control、Expires - 协商缓存:
Last-Modified/If-Modified-Since、ETag/If-None-Match
浏览器处理缓存的基本流程:
用户请求资源 → 检查强缓存是否有效 → 有效则使用缓存
↓ 无效
向服务器发送请求(带协商缓存头)
↓
服务器验证缓存是否过期 → 未过期返回304
↓
过期则返回新资源(200)
三、强缓存详解
3.1 工作原理
强缓存阶段,浏览器不会与服务器通信,直接从本地缓存中读取资源。
关键HTTP头部:
- Cache-Control(HTTP/1.1标准)
Cache-Control: max-age=3600
max-age=3600:资源在3600秒内有效no-cache:禁用强缓存,需向服务器验证no-store:完全不缓存public:允许任何缓存方缓存private:只允许客户端缓存s-maxage:针对共享缓存的时间
- Expires(HTTP/1.0标准)
Expires: Wed, 21 Oct 2025 07:28:00 GMT
- 指定资源的绝对过期时间
- 受客户端和服务器时间差异影响,优先级低于Cache-Control
3.2 实际流程示例
请求:GET /app.js
响应头:
Cache-Control: public, max-age=3600
Expires: Wed, 21 Oct 2025 08:00:00 GMT
用户在3600秒内再次请求该资源时,浏览器检查:
- 计算当前时间与缓存时间的差值是否小于max-age
- 如果小于,直接使用本地缓存(状态码200,from disk cache/memory cache)
- 如果大于,进入协商缓存阶段
注意:Chrome浏览器中,强缓存会显示:
from disk cache:磁盘缓存from memory cache:内存缓存
四、协商缓存详解
4.1 工作原理
当强缓存失效时,浏览器携带缓存标识向服务器请求验证。如果资源未改变,服务器返回304,浏览器使用本地缓存。
两种验证机制:
- Last-Modified / If-Modified-Since
# 第一次请求响应
Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT
# 后续请求请求头
If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT
- 基于文件最后修改时间
- 问题:1秒内多次修改无法识别,时间不精确
- ETag / If-None-Match
# 第一次请求响应
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
# 后续请求请求头
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
- 基于文件内容生成的哈希值
- 优先级高于Last-Modified
- 强ETag:字节级变化就会改变
- 弱ETag(W/前缀):允许语义不变的内容变化
4.2 服务器验证流程
服务器收到带有If-None-Match/If-Modified-Since的请求:
1. 检查If-None-Match是否存在
- 存在:比较ETag是否匹配
- 匹配 → 返回304 Not Modified
- 不匹配 → 返回200和新资源
2. 无ETag,检查If-Modified-Since
- 存在:比较修改时间
- 资源未修改 → 返回304
- 已修改 → 返回200和新资源
3. 两者都无,返回200和新资源
五、完整缓存流程图解
用户请求资源
↓
检查本地是否有缓存
↓
├── 无缓存 ──→ 正常请求服务器 → 返回资源(200)
↓
└── 有缓存 ──→ 检查Cache-Control/Expires
↓
强缓存有效? ──是─→ 使用本地缓存(200 from cache)
↓
否
↓
┌───────────────────────────┐
↓ ↓
If-None-Match/ETag If-Modified-Since/Last-Modified
↓ ↓
服务器验证ETag 服务器验证修改时间
↓ ↓
└───────── 匹配/未修改 ──────┘
↓
返回304 Not Modified → 使用本地缓存
↓
不匹配/已修改
↓
返回200和新资源 → 更新缓存
六、缓存策略最佳实践
6.1 不同资源类型的缓存策略
// 1. 静态资源(JS/CSS/图片) - 强缓存+文件名hash
Cache-Control: public, max-age=31536000
// 文件名带hash:app.a1b2c3.js
// 2. HTML文件 - 协商缓存
Cache-Control: no-cache
// 或短时间缓存
Cache-Control: max-age=0, must-revalidate
// 3. API接口 - 按需缓存
Cache-Control: no-cache, private
// 或短时间缓存
Cache-Control: max-age=60
6.2 缓存更新策略
// 方案1:文件名hash(推荐)
// app.旧hash.js → app.新hash.js
// 强缓存一年
// 方案2:查询参数
// app.js?v=1 → app.js?v=2
// 部分CDN可能不识别查询参数
// 方案3:清除缓存
// Cache-Control: no-cache, no-store, must-revalidate
七、浏览器缓存位置
-
Memory Cache(内存缓存)
- 快速读取,标签页关闭即失效
- 较小资源,如样式、脚本
-
Disk Cache(磁盘缓存)
- 持久化存储,大文件
- 根据Cache-Control头部确定存储位置
-
Service Worker Cache
- 可编程缓存API
- 离线应用支持
八、实际开发注意事项
8.1 常见问题
-
缓存污染:错误配置导致缓存旧版本
# 错误:所有JS文件缓存一年 location ~* \.(js|css)$ { expires 1y; } # 正确:带hash的文件长期缓存 location ~* \.[a-f0-9]{8}\.(js|css)$ { expires 1y; } -
CDN边缘缓存:注意CDN可能缓存304响应
-
用户强制刷新:Ctrl+F5会跳过强缓存
8.2 调试技巧
// Chrome DevTools查看缓存状态
1. Network面板 → Disable cache(禁用缓存)
2. 查看Size列:
- (memory cache)/(disk cache):强缓存命中
- 304:协商缓存命中
3. 查看Response Headers中的缓存相关头部
九、总结对比
| 特性 | 强缓存 | 协商缓存 |
|---|---|---|
| 状态码 | 200 (from cache) | 304 Not Modified |
| 是否请求 | 否 | 是 |
| 相关头部 | Cache-Control, Expires | ETag/If-None-Match, Last-Modified/If-Modified-Since |
| 性能 | 最优(无网络请求) | 次优(有请求但无响应体) |
| 时效性 | 可能过期 | 实时验证 |
| 适用场景 | 长期不变静态资源 | 经常变化资源 |
实际应用中通常组合使用:
Cache-Control: public, max-age=3600
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
这样的配置让浏览器在3600秒内使用强缓存,过期后通过ETag验证,实现性能和实时性的平衡。