HTTP缓存控制
Cache-Control
通用消息头字段,被用于在http请求和响应中,通过指定指令来实现缓存机制。缓存指令是单向的,这意味着在请求中设置的指令,不一定被包含在响应中。
可缓存性指令
- private:私有缓存,表明响应只能被单个用户缓存,不能作为公共缓存(即代理服务器不能缓存它)
- public:公共缓存,表明响应可以被任何对象缓存(包括:发送请求的客户端、代理服务器等)
- no-cache:强制确认缓存,在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证
- no-store:客户端不存储任何内容,每次都会向服务端发起请求
过期性指令
- max-age:设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒),属于强制缓存
- s-maxage:覆盖max-age或者Expires头,但是仅适用于共享缓存(比如各个代理)
Expires
设置缓存的到期时间,属于强制缓存,优先级低于max-age
或者s-maxage
Last-Modified/If-Modified-Since
Last-Modified
是一个响应头部,其中包含源头服务器认定的资源做出修改的日期及时间。
If-Modified-Since
是一个请求头部,服务器只在所请求资源的修改时间在给定的时间之后才会将资源返回,状态码为200
。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的304
响应,而在Last-Modified
头部中会带有上次修改时间。 不同于If-Unmodified-Since
,If-Modified-Since
只可以用在 GET 或 HEAD 请求中。
Last-Modified/If-Modified-Since
是基于客户端和服务端的协商缓存机制,需要和Cache-Control
配合使用。客户端第一次请求资源的时候,服务端返回Last-Modified
,客户端将返回保存下来,当客户端第二次请求资源的时候,若资源过期,将发送If-Modified-Since
请求头,值为第一次请求返回头的Last-Modified
,服务端根据资源修改时间是否是If-Modified-Since
之后来判断客户端缓存是否过期,如果过期,则返回200的资源,若没有过期,则返回304,客户端直接使用缓存。
缺点:
- 某些服务器无法获取资源的精确修改时间
- 存在文件修改时间变化,但是文件内容没有变化的情况
ETag/If-None-Match
ETag
是HTTP响应头是资源的特定版本的标识符。Etags类似于指纹,也可能被某些服务器用于跟踪。
If-None-Match
是一个条件式请求首部。对于GET
和HEAD
请求方法来说,当且仅当服务器上没有任何资源的ETag
属性值与这个首部中列出的相匹配的时候,服务器端会才返回所请求的资源,响应码为200
。
ETag/If-None-Match
的协商机制和Last-Modified/If-Modified-Since
类似,优先级比Last-Modified
高,不同的是ETag
的值是根据文件内容进行MD5生成的。只要文件内容变化,则ETag
的值也会变化。这种方式比Last-Modified/If-Modified-Since
更加精确。
HTTP缓存工作流程
是时候祭出这张图了。图来源于:彻底弄懂HTTP缓存机制及原理
当浏览器请求资源时,先检查是否有缓存,如果没有缓存,则直接向服务端发起请求,如果存在缓存,则会进入如下的流程:
- 先通过
max-age
、Expires
等来判断缓存是否过期,如果缓存没有过期,则直接使用缓存的副本,这是一个强制缓存的过程;如果缓存过期,则进入协商缓存的过程 - 协商缓存首先判断缓存是否存在
ETag
或者Last-Modified
,如果都不存在,则会向服务端发起新请求;如果存在其中一个,或者都存在(ETag
的优先级高于Last-Modified
),则向服务端发起请求,并携带If-None-Match
或者If-Modified-Since
,值为缓存的ETag
/Last-Modified
,服务端接收到请求,会判断当前资源的ETag
/Last-Modified
的值和请求的If-None-Match
/If-Modified-Since
的值是否符合要求,若符合,则返回304,告知客户端缓存资源未过期,可继续使用缓存副本,否则,则返回200,并把当前的新资源返回,告知客户端缓存过期,使用新的资源。
强制缓存命中
第一次请求:
第二次请求: 可以从图中看出,强制缓存命中时,status显示200
,size显示memory cache
,time显示0 ms
。
协商缓存命中
第一次请求:
第二次请求: 可以从图中看出,协商缓存命中时,status显示304
,size明显比第一次请求小很多,time也比第一次请求要快。
HTTP缓存策略
- 对于所有可缓存资源,需指定
Etag
/Last-Modified
,实现协商缓存 - 对于URI可变的静态资源,可通过URI加标识(内容hash或者版本号)并且设置较长过期时间(
max-age
/Expires
)实现强制缓存 - 对于动态资源,可指定
no-store
来实现不缓存