全面解析HTTP缓存机制:从浏览器缓存到代理缓存
1. 缓存体系概述
HTTP缓存体系主要分为三类:
- 浏览器缓存(私有缓存):存储在用户浏览器中,仅对单个用户有效
- 代理缓存(共享缓存):位于客户端和服务器之间的缓存(如CDN、反向代理、网关等),可为多个用户提供服务
- 混合缓存:Service Worker等现代混合缓存机制
2. 强缓存(本地+代理)
强缓存阶段不发送请求到源服务器,直接使用缓存副本。
控制字段
Cache-Control (HTTP/1.1)
public:响应可被任何缓存(包括浏览器和代理)存储private:响应只能被浏览器缓存(不允许代理缓存)max-age=<seconds>:缓存有效期(相对时间)s-maxage=<seconds>:专门设置代理缓存的有效期(优先级高于max-age)no-store:禁止任何缓存no-cache:不使用强缓存,立即进入协商缓存immutable:资源永不变(仅限浏览器缓存)
Expires (HTTP/1.0)
- 绝对过期时间(如
Expires: Thu, 31 Dec 2037 23:55:55 GMT) - 缺点:依赖客户端时间同步
代理缓存特殊处理
1 | Cache-Control: public, max-age=3600, s-maxage=7200 |
表示:
- 浏览器缓存1小时(3600秒)
- 代理缓存2小时(7200秒)
强缓存生效流程
1 | graph TD |
3. 协商缓存
当强缓存失效时,客户端携带验证信息向服务器确认资源有效性。
验证机制
1. Last-Modified / If-Modified-Since
- 服务器响应头:
Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT- 生成方式:文件系统中资源的最后修改时间
- 客户端请求头:
If-Modified-Since: <Last-Modified-value>- 流程:如果服务器资源修改时间 > 客户端提供的值,则返回新资源(200);否则返回304
2. ETag / If-None-Match
- 服务器响应头:
ETag: <etag_value>- 生成方式:
- 强ETag:字节级匹配(如
"5d83c2-55e-7f7163f3b5d00") - 弱ETag:语义匹配(如
W/"5d83c2-55e-7f7163f3b5d00")
- 强ETag:字节级匹配(如
- 常见生成算法:
1
2# Nginx配置示例(默认使用弱校验)
etag on; # 默认基于最后修改时间+内容长度计算1
# Apache默认使用inode+修改时间+大小
- 生成方式:
- 客户端请求头:
If-None-Match: <etag_value>- 流程:服务器计算当前ETag与客户端值匹配则返回304,否则返回200
对比分析
| 维度 | ETag | Last-Modified |
|---|---|---|
| 精确度 | 高(内容变化即可检测) | 低(1秒内修改无法感知) |
| 性能消耗 | 高(需读取完整内容) | 低(仅文件属性) |
| 分布式问题 | 无(内容决定) | 有(服务器时间需同步) |
| 优先级 | 高(两者共存时优先使用) | 低 |
| 典型场景 | 频繁修改的小文件(如API) | 大型静态文件 |
协商缓存流程
1 | sequenceDiagram |
304状态码详解
- 语义:Not Modified(资源未修改)
- 特点:
- 无响应体(节省带宽)
- 必须包含更新的缓存头(如Date等)
- 效果:指示客户端重用现有缓存
4. 缓存策略实践场景
不同资源类型的缓存配置
| 资源类型 | 推荐策略 | 场景说明 |
|---|---|---|
| HTML文档 | Cache-Control: no-cache + ETag |
内容频繁更新,需及时验证 |
| 哈希版本JS/CSS | Cache-Control: max-age=31536000, immutable |
文件名含哈希,内容永不变 |
| 静态媒体资源 | Cache-Control: public, max-age=604800 |
图片/字体等变更较少 |
| API响应 | Cache-Control: private, max-age=600 + ETag |
用户私有数据,中等新鲜度要求 |
| CDN托管资源 | Cache-Control: public, max-age=86400, s-maxage=2592000 |
代理缓存比浏览器缓存更久 |
5. 预加载技术
<link rel="preload">
目的:声明当前页面关键资源,提前加载
1 | <!-- 预加载关键字体 --> |
特点:
- 资源优先级提升为High
- 必须指定
as属性(font/image/script/style等) - 浏览器立即发起请求
<link rel="prefetch">
目的:预测用户下一步操作,提前缓存资源
1 | <!-- 预取下一页资源 --> |
特点:
- 优先级为Lowest
- 浏览器空闲时加载
- 缓存时间短(Chrome默认5分钟)
使用场景对比
| 技术 | 最佳场景 | 风险提示 |
|---|---|---|
preload |
关键渲染路径资源(首屏字体/关键CSS) | 过度使用会挤占带宽 |
prefetch |
用户可能访问的下一页(产品详情/文章页) | 预测错误导致资源浪费 |
6. Service Worker缓存
核心能力
- 完全控制网络请求(拦截/修改/缓存)
- 离线运行能力
- 后台同步
缓存策略实现
1 | // 示例:Stale-While-Revalidate策略 |
典型应用场景
- 离线应用:缓存核心资源实现离线访问
- 性能优化:缓存API响应提升二次访问速度
- 降级方案:网络不可用时提供基础功能
- 资源更新:后台静默更新缓存资源
7. 代理缓存详解
工作特性
- 位置:客户端与源服务器之间
- 优势:
- 减少源服务器负载
- 降低网络延迟(就近访问)
- 抵御流量高峰
- 缓存控制:
- 遵循
Cache-Control的public/s-maxage指令 - 可忽略
Vary头(某些代理)
- 遵循
代理缓存失效
- 传统方式:等待缓存过期
- 主动清除:
- CDN管理界面清除缓存
- 通过API触发刷新(如
POST /purge)
- 内容变更:
- 修改URL(推荐)
- 使用缓存破坏参数(需配置代理支持)
8. 高级缓存问题
缓存中毒(Cache Poisoning)
- 成因:代理缓存存储了包含用户特定信息的响应
- 防御:
- 正确设置
Vary头(如Vary: Cookie, User-Agent) - 敏感内容使用
private
- 正确设置
多版本资源处理
1 | Vary: Accept-Encoding, User-Agent |
指示代理根据Accept-Encoding和User-Agent字段缓存不同版本资源
总结:缓存优先级与决策树
优先级总则
Cache-Control>ExpiresETag>Last-Modifieds-maxage>max-age(对代理缓存)
缓存决策流程
1 | graph TD |
掌握HTTP缓存机制是Web性能优化的核心技能。合理配置浏览器缓存、代理缓存和Service Worker缓存,配合预加载技术,可显著提升用户体验并降低服务器成本。