一文读懂HTTP缓存机制

一句话概况:本地缓存请求到的资源,后续请求尽可能直接复用这些资源,减少Http请求,从而显著提高网站和应用程序的性能。

那么什么时候缓存资源到本地?缓存资源什么时候过期?什么情况下使用这些缓存的资源呢?本文就带着这三个问题开始展开。

HTTP缓存机制流程

一文读懂HTTP缓存机制
从流程中可以看到,浏览器发起资源请求后,大致有三部分:强缓存校验、协商缓存校验、资源请求。本文主要讲解强缓存和协商缓存模块,资源请求部分就是正常的一次HTTP交互过程,但值得注意的是:

因为一般只有GET请求才会被缓存,所以这里泛指一般的GET资源请求。

强缓存

不需要额外向服务端发送请求,直接使用本地缓存。在Chrome浏览器中本地强缓存分为两类,一类是disk cache,一类是memory cache,查看devtools中的Networks会看到请求状态为200,并且后面跟着from disk cachefrom memory cache的请求就是使用了强缓存,如下面两个图。
一文读懂HTTP缓存机制一文读懂HTTP缓存机制

本人也尚未了解Chrome浏览器如何控制两种强缓存,故不展开了,以免误导读者,希望能有高手指出!!!!这里放上找到的Chrome官方文档中的描述,其大体意思是两种强缓存策略与渲染进程的生命周期有关,渲染进程的周期又大致与tab选项卡相对应:

Chrome employs two caches — an on-disk cache and a very fast in-memory cache. The lifetime of an in-memory cache is attached to the lifetime of a render process, which roughly corresponds to a tab. Requests that are answered from the in-memory cache are invisible to the web request API.

是否使用强缓存由HTTP的三个头部字段来控制:ExpiresPragmaCache-Control

Expires

Exipres字段是Http/1.0中的字段,其优先级在三个缓存控制字段中最低。
一文读懂HTTP缓存机制
如图所示,响应头中Expires的值是一个时间戳,发起请求时,如果本地系统时间在这个时间戳之前,则缓存有效,否则缓存失效,进入协商缓存。若该响应头中Expires设置为无效的日期,比如 0, 则代表着过去的日期,即该资源已经过期。

Cache-Control

一文读懂HTTP缓存机制
Cache-ControlHTTP/1.1 中规定的通用头部字段,常用属性如下:

  • no-store:禁止使用缓存,每次请求都去服务端拿最新的资源;
  • no-cache:不使用强缓存,直接进入协商缓存模块,向服务端请求校验资源是否“新鲜”;
  • private:私有缓存,中间代理服务端不可缓存资源
  • public:公共缓存,中间代理服务端可以缓存资源
  • max-age:单位:秒,缓存的最长有效时间。其起始时间为缓存时响应头中的Date字段,即有效期到responseDate + max-age,发起请求时超过该时间则缓存过期。
  • must-revalidate:缓存一旦过期,则必须重新向服务端验证。

Pragma

PragmaHTTP/1.0 中规定的通用头部字段,用于向后兼容只支持 HTTP/1.0 协议的缓存服务端。这个字段只有一个值:no-cache,其表现行为与Cache-Control: no-cache一致,但是HTTP的响应头没有明确定义这个属性,所以它不能拿来完全替代HTTP/1.1中定义的Cache-control头。

如果PragmaCache-Control 两个字段同时存在,Pragma的优先级大于Cache-Control

协商缓存

当强缓存过期或者请求头字段设置不走强缓存,比如Cache-Control:no-cachePragma:no-cache,则进入协商缓存部分。协商缓存涉及两对头部字段,分别是Last-Modified/If-Modified-Since、和ETag/If-None-Match

若请求头中携带If-Modified-SinceIf-None-Match字段,则会发起去服务端校验资源是否有变化,如果有变化,则未命中缓存,服务端返回200,浏览器计算响应体资源是否缓存并使用资源;如果未变换,则命中缓存,返回304,浏览器根据响应头更新缓存头部信息,延长有效期,并直接使用缓存。
一文读懂HTTP缓存机制

Last-Modified/If-Modified-Since

Last-Modified/If-Modified-Since的值是资源修改时间。第一次请求资源时,服务端将资源的最后修改时间放到响应头的 Last-Modified 字段中,第二次请求该资源时,浏览器会自动将该资源上一次响应头中的Last-Modified的值放到第二次请求头的If-Modified-Since字段中,服务端比较服务端资源的最后一次修改时间和请求头中的If-Modified-Since 的值,如果相等,则命中缓存返回 304,否则,返回200。

ETag/If-None-Match

ETag/If-None-Match 的值是一串hash值(hash算法不统一),是资源的标识符,当资源内容发生变化,其hash值也会改变。其过程与上面的相似,不过服务端是比较服务端资源的hash值和请求头中的If-None-Match的值,但比较方式有所区别,因为ETag有两种类型:

  • 强校验:资源hash值具有唯一性,一旦变化则hash也变化。
  • 弱校验:资源hash值以W/开头,若资源变化较小,则同样可能命中缓存。

例如下面这样:

ETag: “33a64df551425fcc55e4d42a148795d9f25f89d4”
ETag: W/“0815”

两者区别

  1. ETag/If-None-Match优先级比Last-Modified/If-Modified-Since高;
  2. Last-Modified/If-Modified-Since有个1S问题,即服务端在1S内修改文件,且再次受到请求时,会错误的返回304

代理服务缓存

一文读懂HTTP缓存机制
VaryHTTP/1.1中的一个头字字段,其值为请求头中的字段,如上图中的Accept-Encoding,可以是多个,以逗号分割,其记录了代理服务器返回资源参考了哪些请求头字段。代理服务器拿到源服务器的响应报文,会根据 Vary 里的字段列表,缓存不同版本的资源。当有资源请求再次访问时,代理服务器会分析请求头字段,返回正确的版本。

总结

在实际应用过程,对一些更新不频繁的资源合理使用缓存机制,可以有效提高系统的响应速度,提高用户体验。