HTTP 缓存:提升 Web 性能的核心机制
HTTP 缓存是优化 Web 性能的关键技术,通过在客户端(如浏览器)存储已请求的资源副本,减少重复向服务器请求的次数,从而降低网络延迟、节省带宽并提升用户体验。其核心逻辑是:客户端首次请求资源时缓存数据,后续请求优先使用缓存(若有效),否则向服务器重新获取。
HTTP 缓存的基本原理
HTTP 缓存的工作流程可概括为:
- 首次请求:客户端向服务器请求资源,服务器返回资源并在响应头中附加缓存规则(如过期时间、修改标识)。
- 缓存存储:客户端将资源和缓存规则一起保存到本地缓存(如浏览器的内存、磁盘)。
- 后续请求:客户端先检查本地缓存是否有效:
- 若缓存有效,直接使用缓存数据(缓存命中);
- 若缓存无效,向服务器重新请求资源,并更新缓存(缓存失效)。
HTTP 缓存的两类核心规则
HTTP 缓存通过响应头中的字段定义缓存策略,主要分为基于过期时间的缓存和基于资源修改的缓存两大类,两者可协同工作。
基于过期时间的缓存:Expires 与 Cache-Control
这类规则通过指定资源的 “有效期”,让客户端直接判断缓存是否可用,无需向服务器确认,属于强缓存(不发起网络请求,直接使用缓存)。
(1)Expires(HTTP/1.0)
- 作用:响应头字段,值为一个具体的 GMT 时间(如
Expires: Wed, 23 Aug 2025 12:00:00 GMT
),表示资源的过期时间。 - 判断逻辑:客户端下次请求时,若当前时间早于Expires 值,则直接使用缓存;否则认为缓存失效,需重新请求服务器。
- 局限性:
- 依赖客户端本地时间,若客户端时间与服务器时间不一致(如时区偏差、时钟错误),会导致缓存判断失效。
- 仅在 HTTP/1.0 中使用,HTTP/1.1 中被
Cache-Control
替代。
(2)Cache-Control(HTTP/1.1)
Cache-Control
是 HTTP/1.1 中定义的核心缓存控制字段,优先级高于Expires
,支持更灵活的缓存策略,其取值可组合使用。
常见取值及含义:
public
:客户端和代理服务器(如 CDN)都可缓存该资源(适合公开资源)。private
:仅客户端可缓存,代理服务器不可缓存(适合用户私有数据,如个人主页)。max-age=xx
:资源的有效期为xx
秒(从请求成功时开始计时),优先级高于Expires
。例如max-age=3600
表示缓存 1 小时内有效。s-maxage=xx
:仅对代理服务器生效,覆盖max-age
(如 CDN 缓存时间)。no-cache
:缓存有效,但使用前必须向服务器确认资源是否更新(不直接使用缓存,需配合后续验证机制)。no-store
:完全禁止缓存,客户端和服务器都不存储任何资源副本(适合敏感数据,如支付信息)。must-revalidate
:缓存过期后,必须向服务器验证资源有效性,不可使用过期缓存。
示例:
1 | Cache-Control: public, max-age=3600, must-revalidate |
表示资源可被客户端和代理缓存,有效期 1 小时,过期后必须验证是否更新。
基于资源修改的缓存:Last-Modified 与 ETag
当强缓存(Expires
/Cache-Control
)失效时,客户端需向服务器确认资源是否已更新,属于协商缓存(需发起网络请求,但可能不返回完整资源)。
(1)Last-Modified 与 If-Modified-Since
- Last-Modified:服务器在响应头中返回的资源最后修改时间(如
Last-Modified: Tue, 20 Aug 2025 08:00:00 GMT
)。 - If-Modified-Since:客户端后续请求时,在请求头中携带该字段,值为上次获取的
Last-Modified
时间,用于询问服务器 “资源在该时间后是否被修改”。
验证逻辑:
- 若服务器判断资源未被修改(最后修改时间 ≤ If-Modified-Since),返回状态码
304 Not Modified
,不返回资源体,客户端使用缓存。 - 若资源已被修改(最后修改时间 > If-Modified-Since),返回状态码
200 OK
和新资源,客户端更新缓存。
局限性:
- 只能精确到秒级,若资源在 1 秒内多次修改,无法识别。
- 某些操作(如文件重命名但内容不变)会改变修改时间,但资源实际未更新,导致不必要的重新请求。
(2)ETag 与 If-None-Match
为解决Last-Modified
的局限性,HTTP 引入了基于资源内容的唯一标识机制。
- ETag:服务器为资源生成的唯一标识(如
ETag: "abc123"
),资源内容变化时ETag
会更新(无论修改时间是否变化)。 - If-None-Match:客户端后续请求时,在请求头中携带该字段,值为上次获取的
ETag
,用于询问服务器 “资源的ETag
是否与该值匹配”。
验证逻辑:
- 若
ETag
匹配(资源未修改),服务器返回304 Not Modified
,客户端使用缓存。 - 若
ETag
不匹配(资源已修改),服务器返回200 OK
和新资源,客户端更新缓存。
优势:
- 基于内容而非时间判断,更精准(如识别 1 秒内的修改、内容不变的重命名)。
- 支持强验证(精确匹配)和弱验证(
ETag: W/"abc123"
,允许内容轻微修改)。
缓存规则的优先级与协同工作
HTTP 缓存规则的生效顺序如下:
- Cache-Control(HTTP/1.1)优先级最高,若存在则忽略
Expires
。 - 若
Cache-Control
中max-age
未过期,直接使用强缓存。 - 若强缓存失效,客户端发起请求,携带
If-Modified-Since
(对应Last-Modified
)和If-None-Match
(对应ETag
)。 - 服务器优先验证
ETag
(精度更高),再验证Last-Modified
,返回304
或200
。
示例流程:
客户端首次请求
style.css
,服务器返回:1
2
3
4
5HTTP/1.1 200 OK
Cache-Control: max-age=3600
Last-Modified: Tue, 20 Aug 2025 08:00:00 GMT
ETag: "v1.0"
Content-Length: 1024客户端缓存
style.css
,有效期 1 小时。30 分钟后请求:
max-age
未过期,直接使用缓存(不发请求)。2 小时后请求:
max-age
过期,客户端发送请求:1
2
3GET /style.css HTTP/1.1
If-Modified-Since: Tue, 20 Aug 2025 08:00:00 GMT
If-None-Match: "v1.0"服务器判断资源未修改,返回:
1
2HTTP/1.1 304 Not Modified
Cache-Control: max-age=3600客户端继续使用缓存,并更新缓存有效期。
缓存的应用场景与最佳实践
- 静态资源(如 CSS、JS、图片):
- 适用强缓存:设置较长的
max-age
(如max-age=31536000
,1 年)。 - 配合版本控制:资源更新时修改文件名(如
style.v2.css
),避免缓存干扰。
- 适用强缓存:设置较长的
- 动态资源(如 API 接口):
- 禁用强缓存:使用
Cache-Control: no-cache
强制每次验证。 - 启用协商缓存:通过
ETag
或Last-Modified
减少重复传输。
- 禁用强缓存:使用
- 敏感资源(如用户信息、支付页面):
- 禁用缓存:
Cache-Control: no-store
,确保每次从服务器获取最新数据。
- 禁用缓存:
- CDN 缓存:
- 使用
public
和s-maxage
控制 CDN 缓存时间,减轻源服务器压力。
- 使用
v1.3.10