HTTP缓存可以分为强缓存和协商缓存,浏览器请求一个页面的简单流程如下:
- 浏览器会根据资源的 HTTP 头信息来判断是否命中强缓存。如果命中则直接在缓存中获取资源,并不会将请求发送到服务器。
- 如果未命中强缓存,则浏览器会将请求发送到服务器,由服务器来判断资源是否失效,如果失效就将返回最新资源,没有失效就返回 304,这个过程被称为协商缓存。
- 如果未命中协商缓存,会将请求发送到服务器,服务器返回最新资源,并更新缓存。
强缓存
命中强缓存时,浏览器不会发送请求到服务器。请求的返回码是200,但是在 Size 列会显示为 (from cache)
。强缓存是利用 HTTP 的返回头中的 Expires
或者 Cache-Control
两个字段来控制的,用来表示资源的缓存时间。
Expires
缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。也就是说,Expires = max-age + 请求时间
,需要和 Last-modified
结合使用。
Cache-Control
Cache-Control
是一个相对时间,例如 Cache-Control: 3600
,代表着资源的有效期是 3600 秒。由于是相对时间,并且都是与客户端时间比较,所以服务器与客户端时间偏差也不会导致问题。
Cache-Control
与 Expires
可以在服务端配置同时启用或者启用任意一个,同时启用的时候 Cache-Control
优先级高。
Cache-Control
取值:
- max-age: 指定一个时间长度,在这个时间段内缓存是有效的,单位是 s 。
- s-maxage: 同 max-age,覆盖 max-age、Expires,但仅适用于共享缓存,在私有缓存中被忽略。
- public: 表明响应可以被任何对象(发送请求的客户端、代理服务器等等)缓存。
- private: 表明响应只能被单个用户(可能是操作系统用户、浏览器用户)缓存,是非共享的,不能被代理服务器缓存。
- no-cache: 不强制缓存,但是协商缓存。
- no-store: 不缓存
协商缓存
若未命中强缓存,则浏览器会将请求发送至服务器。服务器根据 HTTP 头信息中的 Last-Modify
/ If-Modify-Since
或 Etag
/ If-None-Match
来判断是否命中协商缓存。如果命中,则 HTTP 返回码为304,浏览器从缓存中加载资源。Last-Modified
与 ETag
是可以一起使用的,服务器会优先验证 ETag
,一致的情况下,才会继续比对 Last-Modified
,最后才决定是否返回 304。
Last-Modify / If-Modify-Since
- 浏览器第一次请求一个资源时,服务器的响应头中会添加一个
Last-Modify
是一个时间标识,标识请求资源最后一次变动时间。 - 下次请求时,浏览器端会在请求头中添加
If-Modify-Since
,值是缓存之前返回的Last-Modify
。 - 服务器接收后对比
If-Modify-Since
和资源最后一次修改的时间,如果没变化则命中协商缓存,返回 304 ,如果变化就返回最新资源。
ETag / If-None-Match
与 Last-Modify
/ If-Modify-Since
不同的是,Etag
/ If-None-Match
返回的是一个校验码(ETag: entity tag
)。ETag
可以保证每一个资源是唯一的,资源变化都会导致 ETag
变化。ETag
值的变更则说明资源状态已经被修改。服务器根据浏览器上发送的 If-None-Match
值来判断是否命中缓存。
用户行为与缓存
浏览器缓存行为还有用户的行为有关,当进行某些操作是,是强制不缓存的:
用户操作 | Expires / Cache-Control | Last-Modified / Etag |
---|---|---|
地址栏回车 | 有效 | 有效 |
页面链接跳转 | 有效 | 有效 |
新开窗口 | 有效 | 有效 |
前进、后退 | 有效 | 有效 |
F5刷新 | 无效 | 有效 |
Ctrl + F5刷新 | 无效 | 无效 |