HTTP Caching
HTTP缓存作为Web重要的一环,理解并合理使用它能够提高Web性能
前言
- 什么是HTTP缓存
- 为什么需要HTTP缓存,因为使用HTTP缓存可以减少请求响应时间和避免网络时延带来的等待时间
- 如何合理使用缓存,设置不同文件的缓存时间
缓存的种类
缓存分为两种,Private cache和Shared cache
- Private cache是针对单个用户而言,一个例子就是自己的浏览器缓存,使用这些缓存实现了浏览器的前进/后退,而无需再次请求资源
- Shared Cache针对多个用户,比如代理服务器,一个ISP (Internet Service Provider)或者公司都有代理服务器,用来缓存一些资源,供所有员工访问,而不必再向互联网发出请求,避免网络延迟
缓存控制
要清楚缓存控制,这里要先分清两个概念,一个叫做缓存存储(Cache Storage),一个叫做缓存(Caching)
其实在中文里面,可能直接都说成缓存,但是这样不助于理解下面缓存控制的前两种情况。其实缓存存储是一个实实在在的东西,而缓存只是它的一个动作
后来我又发现国人有强缓存和协商缓存的说法,强缓存就是指Cache Storage会直接返回Cache的内容,协商缓存是指Cache Storage向原始服务器进行验证,如果原始服务器返回304 Not Modified,Cache Storage才返回Cache的内容
淘宝网首页, Chrome Dev Tools下观察到的协商缓存(304)和强缓存 (from xxx cache)
上面一幅图中,304 Not Modified表示使用了协商缓存,而from memory cache和from disk cache则直接使用强缓存
Chrome在某个版本之后将以前的from cache变成了from disk cache和from 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发请求,否则使用缓存,看下面这幅图
在响应头使用Vary字段条件验证Content-Encoding
Vary字段通常会对User-Agent进行验证,避免将移动端缓存的文档直接发给桌面端请求的用户,反之亦然
Vary: User-Agent