HTTP 缓存控制

HTTP 的缓存控制

缓存(cache)是优化系统性能的利器,由于链路长,网络延迟不可控,通过 HTTP 获取网络资源的成本比较高,所以,浏览器采用了缓存机制,将拿到的数据缓存起来,下次再请求的时候尽可能复用,这样的好处也很明显:响应速度快、节省网络带宽。

缓存流程

整个浏览器的缓存是这样的:

  • 首先浏览器发现发送请求,获取服务器资源
  • 服务器响应请求,返回资源,同时标记资源有效期
  • 浏览器缓存资源,等待下次重用

image

强制缓存

服务器标记资源的有效期的头字段是Cache-Control,常用的属性是max-age=xx,意思是最大持续时间多少秒。这个属性在 Cookie 那一节也有类似,只是头字段不一样。

Cookie 的 max-age 是在浏览器收到 Cookie 的时间开始算的,而 Cache-Control的 max-age 则是在服务器发送响应头字段开始计算的。

还有其他的属性例如:

  • no_store:不允许缓存
  • no_cache:可以 缓存但是使用前必须去服务器验证是否过期,是否有最新的版本
  • must-revalidate:意思是缓存不过去就可以继续使用,但是过期了还想用就要去服务器验证。

image

客户端缓存控制

服务器发送了缓存指令,浏览器就会把缓存数据给保存到本地,等到需要用的时候,就会读取本地缓存中的服务器资源,提高网络效率。

不止浏览器可以发 Cache-Control 头,浏览器也可以发,这说明请求-应答双方都可以用Cache-Control 字段进行缓存控制。

当我们点击刷新按钮的时候,浏览器会在请求头里面加Cache-Control:max-age=0,表示的意思是我要最新的资源。这时候不会读取浏览器的缓存,而是向服务器发送请求。

下图是我在chrome浏览器中点击刷新时发送出去的请求头,自动带上了 Cache-Control:no-cache,跟 max-age效果是一样的。
image

浏览器的缓存什么时候生效呢?当我们点击前进、后退按钮的时候,会看到 from disk cache 的字样,这就触发了浏览器的缓存。
image

因为前进、后退等操作,浏览器只会添加最基本的请求头,不会增加Cache-Control之类的字段,所以会触发本地缓存。

协商缓存

使用 Cache-Control 做缓存控制只能刷新数据,不能很好地利用缓存数据,所以这里又有一个协商缓存的概念,也叫条件请求。

它的做法是利用两个连续的请求来组成验证动作,它是这样运作的:

第一步:先发一个 HEAD 请求,获取资源的修改时间或者其他元信息,然后与缓存数据比较,如果没有改就使用缓存数据,如果改了就走第二步。

第二步:发送 GET 请求,获取最新的版本。

为此 HTTP 协议还定义了 If 开头的条件请求字段。服务器第一次响应时需要提供 Last-Modified 和 ETag,然后第二次请求时就可以带上缓存里的原值,验证资源是否是最新的。

如果资源没有变,服务器就回应一个304 Not Modified 的状态码,表示缓存依然有效,浏览器就可以更新一下有效期,然后使用缓存。

image

Last-modified 的意思是文件的最后修改时间。

ETag 是资源唯一标识,主要用来解决修改时间无法准确区分文件变化的问题。

比如说一个文件定期更新,但是有时什么内容都没有发生变化,这时候用 Last-modified 就会误以为发生变化,传给浏览器就会浪费带宽。

使用 ETag可以精确识别资源的变动情况,让浏览器可以有效利用缓存。

其中 Last-modified 对应的常用条件请求字段是 if-Modified-Since,ETag 对应的常用条件请求字段是 If-None-Match。

当服务器第一次发送 Last-modified 给浏览器时,浏览器就把资源缓存下来,然后下次请求时在HEAD请求的头部带上 if-Modified-Since:第一次的 Last-modified 的值给服务器,服务器经过对比,告诉浏览器我这里的文件都是那段时间以前的,于是浏览器就使用缓存加载资源。

ETag 同理,只是这时候请求头的字段换成了If-None-Match

小结

缓存是优化系统性能的重要手段,HTTP 传输时每个环节都可以使用到缓存

服务器使用 Cache-Control 来设置缓存策略,常用的是 max-age,表示资源有效期

浏览器收到数据就会存入缓存,如果没过期就可以直接使用,过期就要去服务器验证是否依然可用

验证资源是否失效需要使用条件请求,常用if-Modified-Since If-None-Match,如果返回304就可以使用缓存里面的资源

验证资源是否被修改涉及到两个条件,ETagLast-Modified,需要服务器预先在响应报文中设置,搭配条件请求使用

浏览器也可以发送 Cache-Control,比如刷新操作,就会发送 max-age=0来刷新数据

浏览器缓存的一个中心思想就是:没有请求的请求,才是最快的请求