Cache-Control
Cache-Control是HTTP1.1中引入的一种通用头部字段,可以用于请求和响应头部;现代浏览器都支持Cache-Control,主要用于替代HTTP1.0中定义的一些相应缓存标头,如Expires、Pragma等;客户端和服务端的请求头有一些差异:
指令 | 客户端 | 服务端 |
---|---|---|
no-cache | √ | √ |
no-store | √ | √ |
max-age=<seconds> | √ | √ |
no-transform | √ | √ |
public | × | √ |
private | × | √ |
max-stale | √ | × |
only-if-cached | √ | × |
must-revalidate | × | √ |
proxy-revalidate | × | √ |
s-maxage=<seconds> | × | √ |
-
no-cache
和no-store
:
no-store
禁止浏览器及所有的中间件(代理服务器等)缓存任何版本的响应信息,即我们通常所说的不缓存,每次请求都需要向服务器发送完整地请求并下载相应的响应;no-cache
不直接使用缓存,需要先在与服务器确认返回的响应是否发生变化。即如果存在ETag(如ETag: af47a1d)等验证令牌时,会发送请求(请求带着If-None-Match: af47a1d标头)到服务器检验之前的缓存内容和当前版本有无变化,如果无变化则返回响应状态码304,否则重新请求并下载资源; -
public
与private
:
private
:响应仅对单个用户进行缓存,也就是说对于代理服务器、CDN等中间缓存机构不进行缓存;
public
:非必须项,表示响应在多数情况下均可以被缓存(包括CDN、代理服务器等); -
only-if-cached
:客户端标头,表示客户端仅接受已缓存的响应,并且不会检测当前响应的新鲜度。 -
max-age: <seconds>
:从请求时间开始计算的缓存存储的最大有效时长,单位为秒;超过则为过期; -
max-stale[=<seconds>]
:客户端标头,表示客户端可以接受一个过期资源;如果设置了可选的时间参数,则表示过期时间不超过该时长; -
must-revalidate
:服务端标头,使用缓存前需要验证旧资源的状态,过期资源不可用; -
proxy-revalidate
:服务端标头,仅适用于共享缓存(如代理等),作用与上类似;
// 禁止缓存
Cache-Control: no-cache, no-store, must-revalidate
// 缓存静态文件
Cache-Control: public, max-age=31536000
下图是MDN推荐的最佳Cache-Control策略:
Pragma
Pragma是HTTP1.0中定义的通用首部字段,与HTTP1.0中的Expires标头共同决定HTTP1.0中的缓存策略;Pragma: no-cache
作用与Cache-Control: no-cache
作用一致;现在通常用与对HTTP1.0客户端的兼容;
Expires
Expires是HTTP1.0及HTTP1.1都有的响应头部字段,表示启用缓存并设置资源过期的时间点,包含日期、时间;设置过去的时间则表示资源过期;同时设置Pragma和Expires则不缓存;这里需要注意的是服务器时间和本地时间的统一问题,否则可能导致合法的资源被当做过期处理,在项目中已采坑。。
如果响应头部中包含
Cache-Control:max-age
或Cache-Control:s-max-age
字段,则Expires字段会被忽略;
// 示例
Expires: Wed, 21 Oct 2015 07:28:00 GMT
缓存校验机制
如果由于服务器时间精度问题;或者如果设置的缓存时间已经过期,并且服务端资源并未发生更新,仅使用max-age
等类似的缓存过期判断策略,会重新下载整个并未发生变化的资源,这会造成带宽浪费与服务器负载压力;
Last-Modified与If-Modified-Since
Last-Modified
:响应头部字段;表示服务器端资源上次修改的日期和时间,通常与请求字段If-Modified-Since
搭配使用;其精度低于ETag,故常作为备用机制;// 用法 Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT // 示例 Last-Modified: Wed, 20 May 2019 11:11:11 GMT
If-Modified-Since
:请求字段(GET/HEAD方式),如果请求的资源在给定时间后未被修改,那么返回一个不带响应主体的304响应;否则是一个200的请求;同样,如果同时设置了If-None-Match
,If-Modified-Since
也会被忽略;// 示例 If-Modified-Since: Wed, 20 May 2019 11:11:11 GMT
ETag与If-None-Match
Last-Modified
的精度与服务器的时间精度有关,无法识别一秒内进行多次修改的情况,也就是说资源的修改时间有可能存在一定偏差,而ETag可以精确标识资源的改动;另外,如果资源修改而其内容未发生变化(修改后再恢复)的情况下还是会重新加载资源;
-
ETag相当于资源的摘要,值是一段ASCII码组成的字符串,仅在其内容发生变化时对应的ETag值才会改变;这样就可以避免
Last-Modified
中资源被修改而内容未发生变化的状况了;ETag的生成方式不唯一;服务器响应资源请求时,可以在响应头部加上ETag指令,如
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
,客户端可以选择是否缓存该资源及标识并且在下次请求该资源时在请求头中加入一个If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
的头部字段;服务器将当前版本的ETag与客户端的ETag字段值进行比较,如果两者保持一致则表示资源未被更改,返回304;需要注意的是,上边已经提到了ETag生成的方式不唯一,可以通过hash散列或者其他算法获得;因此在使用CDN或者其他分布式服务器系统时,需要保障ETag的唯一性,从而避免不必要的资源请求;当然,计算ETag对服务器性能也有一定的影响;
W/
为可选参数,用于标识是否为弱校验;ETag支持强校验和弱校验:强校验需要资源每个字节都对应相同,包括Content-Type、Content-Encoding等;弱校验仅需二者在语义上相同,即可以使用不同的Content-Type值等,尤其是在动态生成内容上弱校验更能发挥作用;// 示例 ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4" ETag: W/"0815"
F5与Ctrl+F5
Ctrl+F5会强制不用所有缓存并全部向服务器发送新的请求,请求头中移除了If-Modified-Since
及If-none-Match
标头,并且加入Cache-Control: no-cache
及Pragma: no-cache
;
F5则会加入请求头If-Modified-Since
、If-none-Match
及Cache-Control: max-age=0
请求头以有效使用缓存;
文献5的图更形象地表明了客户端状态码与请求头部的关系:
结合上图,为了更高效地利用HTTP缓存,应当尽量为可缓存资源设置Expires/Cache-Control及ETag/Last-Modified标头,这样可以能减少304请求造成的性能开销,同时能在资源更新的时候尽可能早的获取新版本的资源,从而使用户体验更好;现在,使用webpack构建项目时,通常会将静态资源的文件名后缀加上md5等,这样可以在资源有新版本时直接更新资源,减少304的开销;
强缓存与协商缓存
强缓存
:由expires
与cache-control: max-age
控制;表示在缓存有效期内不需要重新请求服务端资源,返回的状态码为200
;协商缓存
:由ETags
与If-No-Match
、Last-Modified
与If-Modified-Since
控制;在缓存过期时,会用到协商缓存;协商缓存需要向浏览器发送请求,以确认当前过期的资源是否发生过更新,如果没有更新,即缓存有效则返回304
;否则重新获取资源;
参考文献:
3. https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control
4. https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching_FAQ
5. https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn
6. https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Pragma
7. https://imweb.io/topic/5795dcb6fb312541492eda8c
8. https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Last-Modified
9. https://zh.wikipedia.org/wiki/HTTP_ETag