HTTP Caching

September 16, 2019 by Tian Zhi

HTTP缓存作为Web重要的一环,理解并合理使用它能够提高Web性能

前言

  • 什么是HTTP缓存
  • 为什么需要HTTP缓存,因为使用HTTP缓存可以减少请求响应时间和避免网络时延带来的等待时间
  • 如何合理使用缓存,设置不同文件的缓存时间

缓存的种类

缓存分为两种,Private cacheShared cache

  • Private cache是针对单个用户而言,一个例子就是自己的浏览器缓存,使用这些缓存实现了浏览器的前进/后退,而无需再次请求资源
  • Shared Cache针对多个用户,比如代理服务器,一个ISP (Internet Service Provider)或者公司都有代理服务器,用来缓存一些资源,供所有员工访问,而不必再向互联网发出请求,避免网络延迟

http cache type

缓存控制

要清楚缓存控制,这里要先分清两个概念,一个叫做缓存存储(Cache Storage),一个叫做缓存(Caching)

其实在中文里面,可能直接都说成缓存,但是这样不助于理解下面缓存控制的前两种情况。其实缓存存储是一个实实在在的东西,而缓存只是它的一个动作

后来我又发现国人有强缓存协商缓存的说法,强缓存就是指Cache Storage会直接返回Cache的内容,协商缓存是指Cache Storage向原始服务器进行验证,如果原始服务器返回304 Not ModifiedCache Storage才返回Cache的内容

chrome memeory cache disk cache 淘宝网首页, Chrome Dev Tools下观察到的协商缓存(304)和强缓存 (from xxx cache)

HTTPStaleness 强缓存和协商缓存验证资源是否过期

上面一幅图中,304 Not Modified表示使用了协商缓存,而from memory cachefrom disk cache则直接使用强缓存

Chrome在某个版本之后将以前的from cache变成了from disk cachefrom memory cache,就跟名字一样,缓存在磁盘中的内容即使在关闭了当前tab之后还能使用,但是缓存到内存的内容,当关闭tab或者浏览器,或者浏览器崩溃后,就消失了。但是memory cache加载速度更快,从上图也能看出

关于强缓存和协商缓存,我没发现这两个词的英文出处,但是这两个词理解起来更形象,所以下面的缓存控制情况我也会用到这两个词

1. 不使用缓存存储

可以理解为每次的请求都不经过Cache Storage,直接发给服务器,返回新的对象

Cache-Control: no-store
Cache-Control: no-cache, no-store, must-revalidate

2. 不直接使用缓存内容

Cache Storage会缓存内容,但是每次需要向服务器验证资源是否过期,没过期才能使用缓存,这就是304 Not Modified的情况,也就是我们所说的协商缓存

Cache-Control: no-cache

3. 公共缓存和私有缓存

例如告诉资源只能被Private Browser Cache缓存起来还是同时也能被Public Proxy Server缓存起来

Cache-Control: private
Cache-Control: public

4. 强缓存过期控制

Cache-Control优先级高于Expired,前者是相对时间,后者是绝对时间;前者为General Header的字段,后者为Response Header的字段

Cache-Control: max-age=31536000 
Expires: Wed, 21 Oct 2015 07:28:00 GMT

5. 资源验证

Cache-Control: must-revalidate

缓存验证

缓存验证发生在用户刷新,或者响应头中包含Cache-Control: must-revalidate的情况

缓存验证有两种形式,都可以用来验证文档是否过期

  • 文档的最后修改日期,响应头Last-Modified字段,请求头If-Modified-Since字段请求验证
  • 对文档当前版本的唯一标识,叫做entity tag,响应头ETag字段,通常使用MD5 Hash实现,请求头If-None-Match字段请求验证

同时,缓存验证有两种验证类型

  • 强验证(strong validator),文档对比一个字节一个字节地进行,严格对比
  • 弱验证(weak validator),文档只有语义不同才认为是改变,例如一个HTML文档也许只是里面的广告发生改变,或者是页脚的日期发生改变,弱验证会认为它们还是相同的

HTTP默认使用的是强验证

需要注意的是,一般情况下,Last-Modified字段只能作为弱验证,因为它的最小单位是秒,一秒内文档可能改变2次,无法做到标识每一次改变,如果使用它做强验证,则需要明确知道不会发生这种情况。当然,还有一些其他的限制,具体的限制可以看我下面的第4条RFC文档参考,这里就不细说了

ETag默认作为强验证,如果需要用它实现弱验证比较困难,因为需要判断文档中不同元素的语义,确保语义变化时ETag的值才发生变化,而不是每次检测到字节变化时就生成一个新的值

缓存验证这部分建议有能力的同学直接查看第4条RFC参考

条件缓存(响应头增加Vary字段)

该字段指出了筛选条件,如果下一次到达Cache Storage的请求中的该条件与缓存时的该条件的值不一样,则重新向真实server发请求,否则使用缓存,看下面这幅图

HTTPVary 在响应头使用Vary字段条件验证Content-Encoding

Vary字段通常会对User-Agent进行验证,避免将移动端缓存的文档直接发给桌面端请求的用户,反之亦然

Vary: User-Agent

参考

  1. https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching
  2. https://stackoverflow.com/questions/44596937/chrome-memory-cache-vs-disk-cache?rq=1
  3. https://developer.mozilla.org/en-US/docs/Web/HTTP/Conditional_requests
  4. Weak and Strong Validators

Feel free to leave me a message @tianzhich