缓存
分类
- 私有缓存
- 共享缓存
操作的目标
- get 请求,状态码为 200
- 301 永久重定向
- 404 错误响应
- 206 不完全响应
- 匹配到一个已被定义 cache 键名的响应(主要为这个,见下文)
旧字段,此处不讨论
Pragma 头 是在 http 1.0 中规定的通用首部,该字段用来向后兼容只支持 http 1.0 协议的缓存服务器,那时 Cache-Control 还未出现
- no-cache:
Program: no-cache与Cache-Control:no-cache效果一致
- no-cache:
Expires 头 该响应头包含日期/时间,在此时间之后响应过期,如果在 Cache-Control 中设置了
max-age或者s-maxage,则会忽略该响应头- 被取代原因 被 Cache-Control 取代的原因:设置的是到期时间,并且要是 GMT 格式的时间,最致命的是它的到期时间是依据系统时间来看的,如果系统时间错误超过了 Expires 的到期时间,那么就会请求不到资源
- 语法:
Expires: <http-date>,参数为一个 http 时间戳
缓存控制(cache-control)
http 1.1 定义 Cache-Control 头用来区分对缓存及值的支持情况,请求头和响应头都支持该属性,根据不同的值来定义缓存策略
可缓存性
- no-store:没有缓存,缓存中不得存储任何关于客户端请求和服务器响应的内容
- no-cache:缓存但重新认证,每次请求发出时,将此请求发送到服务器(该请求应该带有与本地缓存相关的验证字段),服务器验证缓存是否过期,若未过期则使用本地缓存副本(状态码为 304)
- private:私有缓存,只应用于浏览器私有缓存中(默认)
- pubilc:公共缓存,可被中间代理、CDN 等缓存
过期
max-age=<seconds>:单位秒,设置存储的最大周期,超过该时间被认为过期s-maxage=<seconds>:覆盖 max-age 或者 Expires 头,但是仅适用于共享缓存,私有缓存会忽略min-fresh=<seconds>:表示客户端希望获取一个能在指定的秒数内保持其最新状态的响应stale-while-revalidate=<seconds>:表示客户端愿意接收陈旧的响应,同时在后台异步检查新的响应stale-if-error=<seconds>:表示如果新的检查失败,则客户愿意接收陈旧的响应
重新验证和重新加载
- must-revalidate:一旦资源过期,在成功向原始服务器验证之前,缓存不能用该资源响应后续请求
- proxy-revalidate:与 must-revalidate 作用相同,但它仅适用于共享缓存
- immutable:表示响应正文不会随时间改变
其他
- no-transform:不得对资源进行转换或转变
- only-if-cached:表明客户端只接受已缓存的响应,并且不要向原始服务器检查是否有更新的拷贝
If-None-Match
请求头,是一个条件式请求首部(If-Match 字段与该字段语法一致)
- 对于 GET、HEAD 请求,当且仅当服务器上没有任何资源的 ETag 属性值与这个首部中列出的相匹配时,服务器端才会返回所请求的资源,状态码为 200
- 对于其他请求,当且仅当最终确认没有已存在的资源的 ETag 属性值与这个首部中列出的相匹配时,才会对请求进行处理
- 与 If-Modified-Since 一起出现时,优先级较高
语法
If-None-Match: <etag_value>
If-None-Match: <etag_value>, <etag_value>, ...
If-None-Match: *
<etag_value>:见 ETag*:一个特殊值,代表任意资源,只用在进行资源上传时,通常采用 put 方法,来检测拥有相同识别 ID 的资源是否已经上传过了
If-Modified-Since
请求头,是一个条件式请求首部
服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回
语法
If-Modified-Since: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
<day-name>:"Mon","Tue","Wed","Thu","Fri","Sat","Sun"之一,区分大小写<day>:两位数字表示的天数,例如"04" or "23"<year>:4 位数字表示的年份,例如 "1990" 或者"2016"<hour>:两位数字表示的小时数,例如"04" or "23"<minute>:两位数字表示的分钟数,例如"04" or "23"<second>:两位数字表示的秒数,例如"04" or "23"- GMT:国际标准时间。HTTP 中的时间均用国际标准时间表示,从来不使用当地时间
ETag
响应头,资源的特定版本的标志符
语法
ETag: W/"<etag_value>"
ETag: "<etab_value>"
W/:可选(大写的 W),表示使用弱验证器<etag_value>:实体标签唯一地表示所请求的资源,位于双引号之间的 ASCII 字符串,无明确指定生成方法
作用
该字段主要是为了解决之前只有 If-Modified-Since 的缺点
- 一些文件也许会周期性的更改,但是他的内容并不改变,仅仅改变修改时间,这个时候我们并不希望客户端认为这个文件被修改了而重新获取
- 某些文件修改非常频繁,比如在秒以下的时间内进行修改,If-Modified-since 能检查到的粒度是秒级的,这种修改无法判断
- 某些服务器不能精确的得到文件的最后修改时间
Last-Modified
响应头,包含源头服务器认定的资源做出修改的日期及时间,通常被用作一个验证器来判断接收到的或者存储的资源是否一致,精度比 ETag 要低,所以为备用机制,包含有 If-Modified-Since 或 If-Unmodified-Since 首部的条件请求会使用该字段
语法与 If-Modified-Since 一致
tip
在客户端中重新向服务器发起请求时,会有将下面两者的 key 名替换一下
响应头
ETag: '5c20abbd-e2e8'
Last-Modified: Mon, 24 Dec 2018 09:49:49 GMT
再次请求时,请求头
If-None-Matched: '5c20abbd-e2e8'
If-Modified-Since: Mon, 24 Dec 2018 09:49:49 GMT
请求流程图
初次请求

后续请求

强缓存(200)与协商缓存(304)
强缓存(200 缓存(200 OK (from cache)))
强缓存就是给资源设置个过期时间,客户端每次请求资源时都会看是否过期,只有过期才去询问服务器,简单来讲就是浏览器没有与服务器确认,直接使用了浏览器缓存
当浏览器去请求某个文件的时候,服务器在响应头里面对该文件做了缓存配置,缓存时间、缓存类型都由服务端控制,具体表现为:cache-control: max-age=xxx, public, immutable
协商缓存(304 Not Modified)
第二次请求相同 url 时,判断缓存是否过期,如果过期,则发送 If-Modified-Since 和 If-None-Match 字段给服务器,服务器验证该字段,如果资源未变化,则返回 304,简单讲是浏览器与服务器再次确认缓存的有效性
请求流程
浏览器 -> 缓存 -> 服务器
- 首次请求某个 url,服务端返回 200 状态,此时缓存为空
- 浏览器根据响应消息(Cache-Control、Last-Modified、ETag 等字段),来决定是否缓存数据(此处假设缓存)
- 第二次请求时,判断缓存时间是否过期,查找顺序 Cache-Control -> Expires + Date -> Last-Modified
- 如果未过期,则直接使用缓存
- 如果已过期,缓存会先添加 If-None-Match 头,然后将该缓存发送给服务器,服务器判断资源是否更改,来选择返回 304 或者 200
缓存计算
- Cache-Control: max-age=N,N 值就是缓存寿命
- Expires:计算该值与 Date 头的值来判断寿命
- Last-Modified:Date - (Last-Modified / 10) 获取寿命
- 缓存失效时间计算:
缓存使用期 = 响应使用期 + 传输延迟时间 + 停留缓存时间- 响应使用期:可以通过以下两种方式进行计算
- max(0, responseTime - Date):responseTime 浏览器接收到此响应的时间点,Date 为响应头中的属性值
- Age:Age 的值通常接近于 0。表示此对象刚刚从原始服务器获取不久;其他的值则是表示代理服务器当前的系统时间与此应答中的通用头 Date 的值之差(MDN 原文)
- 传输延迟时间:
responseTime - requestTime,requestTime 为发起请求的时间 - 停留缓存时间:表示资源在浏览器上已经缓存的时间,
now - responseTime,now 为客户端当前时间 - 需要注意下面值而导致的时间问题:
- Age:响应头 Age 值
- Date:响应头 Date 值
- requestTime:缓存发起请求的本地时间
- responseTime:缓存收到响应的本地时间
- now:客户端当前时间
- 响应使用期:可以通过以下两种方式进行计算
Vary 响应
该字段用于判断是请求一个新的资源还是使用缓存文件
语法
Vary: *
Vary: <header-name>, <header-name>, ...
说明
*:所有的请求都被视为唯一并且非缓存<header-name>:逗号分隔的一系列 http 头部字段名称,用于确定缓存是否可用
示例
请求头Accept-Encoding: br,缓存里的值为Content-Encoding: gzip,Vary: Content-Encoding,此时无法使用缓存,因为二者的值不同,将会通过服务器重新请求br类型的数据,当再次请求时,因为此时缓存中已经有了br类型,所以会使用缓存