By Noxxxx from https://www.noxxxx.com/?post_type=post&p=487
欢迎分享与聚合,尊重版权,可以联系授权
这是一篇关于浏览器缓存的原理疏解,涉及到浏览器强缓存和协商缓存。
首先我们来看一下 response header 中浏览器缓存字段有哪几个?
强缓存
对于强制缓存来说,header中会有两个字段来标明失效规则(Expires/Cache-Control),指的是当前资源的有效期。
##来自博客园文章页的请求 response header Cache-Control:private, max-age=10 Connection:keep-alive Content-Type:text/html; charset=utf-8 Date:Thu, 17 Aug 2017 09:28:35 GMT Expires:Thu, 17 Aug 2017 09:28:44 GMT Last-Modified:Thu, 17 Aug 2017 09:28:34 GMT Transfer-Encoding:chunked Vary:Accept-Encoding X-Frame-Options:SAMEORIGIN X-UA-Compatible:IE=10
可以看到这里两个缓存字段都用到了。
Cache-Control
常用 cache-directive 值
Cache-directive | 说明 |
---|---|
public | 所有内容都将被缓存 |
private | 内容只缓存到私有缓存中 |
no-cache | 告诉浏览器、缓存服务器,不管本地副本是否过期,使用资源副本前,一定要到源服务器进行副本有效性校验。 |
no-store | 所有内容都不会被缓存到缓存或 Internet 临时文件中 |
must-revalidation/proxy-revalidation | 如果缓存的内容失效,请求必须发送到服务器/代理以进行重新验证 |
max-age=xxx (xxx is numeric) | 缓存的内容将在 xxx 秒后失效, 这个选项只在HTTP 1.1可用, 并如果和Last-Modified一起使用时, 优先级较高 |
使用后的行为参照:
Cache-directive | 打开一个新的浏览器窗口 | 在原窗口中单击 Enter 按钮 | 刷新 | 单击 Back 按钮 |
---|---|---|---|---|
public | 浏览器呈现来自缓存的页面 | 浏览器呈现来自缓存的页面 | 浏览器重新发送请求到服务器 | 浏览器呈现来自缓存的页面 |
private | 浏览器重新发送请求到服务器 | 第一次,浏览器重新发送请求到服务器;此后,浏览器呈现来自缓存的页面 | 浏览器重新发送请求到服务器 | 浏览器呈现来自缓存的页面 |
no-cache/no-store | 浏览器重新发送请求到服务器 | 浏览器重新发送请求到服务器 | 浏览器重新发送请求到服务器 | 浏览器重新发送请求到服务器 |
must-revalidation/proxy-revalidation | 浏览器重新发送请求到服务器 | 第一次,浏览器重新发送请求到服务器;此后,浏览器呈现来自缓存的页面 | 浏览器重新发送请求到服务器 | 浏览器呈现来自缓存的页面 |
max-age=xxx (xxx is numeric) | 在 xxx 秒后,浏览器重新发送请求到服务器 | 在 xxx 秒后,浏览器重新发送请求到服务器 | 浏览器重新发送请求到服务器 | 在 xxx 秒后,浏览器重新发送请求到服务器 |
Cache-Control是关于浏览器缓存的最重要的设置,因为它覆盖其他设置,比如 Expires 和 Last-Modified。另外,由于浏览器的行为基本相同,这个属性是处理跨浏览器缓存问题的最有效的方法。
所以第一个点:Cache-Control 的优先级最高。
Expires
Expires是http1.0提出的一个表示资源过期时间的header,它描述的是一个绝对时间,由服务器返回,Expires 头部字段提供一个日期和时间,响应在该日期和时间后被认为失效。失效的缓存条目通常不会被缓存(无论是代理缓存还是用户代理缓存)返回,除非首先通过原始服务器(或者拥有该实体的最新副本的中介缓存)验证。(注意:cache-control max-age 和 s-maxage 将覆盖 Expires 头部。)
过程:
1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Expires的header
2)浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来(所以缓存命中的请求返回的header并不是来自服务器,而是来自之前缓存的header);
3)浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,拿出它的Expires跟当前的请求时间比较,如果请求时间在Expires指定的时间之前,就能命中缓存,否则就不行。
4)如果缓存没有命中,浏览器直接从服务器加载资源时,Expires Header在重新加载的时候会被更新。
然而它也有一些问题:
它是服务器返回的一个绝对时间,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改下客户端时间,就能影响缓存命中的结果。所以在http1.1的时候,提出了一个新的header,就是Cache-Control,这是一个相对时间,在配置缓存的时候,以秒为单位,用数值表示,如:Cache-Control:max-age=315360000。
过程:
1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Cache-Control的header
2)浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来;
3)浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,根据它第一次的请求时间和Cache-Control设定的有效期,计算出一个资源过期时间,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则就不行。
4)如果缓存没有命中,浏览器直接从服务器加载资源时,Cache-Control Header在重新加载的时候会被更新。
Cache-Control描述的是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断,所以相比较Expires,Cache-Control的缓存管理更有效,安全一些。
这两个header可以只启用一个,也可以同时启用,当response header中,Expires和Cache-Control同时存在时,Cache-Control优先级高于Expires。
先总结一下,至此强缓存有两个头部:Cache-Control / Expires ,控制的粒度比较粗,max-age 是一个相对时间,Expires是一个过期的时间节点。在缓存期内,浏览器是不会发送 Http 请求 而是直接从 cache 中拿资源。
协商缓存
协商缓存是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】这两对Header来管理的。
【Last-Modified,If-Modified-Since】的控制缓存的原理是:
1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Last-Modified的header,这个header表示这个资源在服务器上的最后修改时间
2)浏览器再次跟服务器请求这个资源时,在request的header上加上If-Modified-Since的header,这个header的值就是上一次请求时返回的Last-Modified的值
3)服务器再次收到资源请求时,根据浏览器传过来If-Modified-Since和资源在服务器上的最后修改时间判断资源是否有变化,如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。当服务器返回304 Not Modified的响应时,response header中不会再添加Last-Modified的header,因为既然资源没有变化,那么Last-Modified也就不会改变,这是服务器返回304时的response header
4)浏览器收到304的响应后,就会从缓存中加载资源。
5)如果协商缓存没有命中,浏览器直接从服务器加载资源时,Last-Modified Header在重新加载的时候会被更新,下次请求时,If-Modified-Since会启用上次返回的Last-Modified值。
【ETag、If-None-Match】的出现时为了弥补,【Last-Modified,If-Modified-Since】的不足,文件有变化,但是最后修改时间却没有变化的情况,而当这种情况出现的时候,就会影响协商缓存的可靠性。所以就有了另外一对header来管理协商缓存。
1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上ETag的header,这个header是服务器根据当前请求的资源生成的一个唯一标识,这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间没有关系,所以能很好的补充Last-Modified的问题
2)浏览器再次跟服务器请求这个资源时,在request的header上加上If-None-Match的header,这个header的值就是上一次请求时返回的ETag的值
3)服务器再次收到资源请求时,根据浏览器传过来If-None-Match和然后再根据资源生成一个新的ETag,如果这两个值相同就说明资源没有变化,否则就是有变化;如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化
4)浏览器收到304的响应后,就会从缓存中加载资源。
所以如果 Last-Modified 启用了,最好也使用上 ETag 来确保。
出于性能的考虑,即便上面所说的缓存设置没有配置,浏览器也会根据自己的规则来缓存资源。开发时,建议勾选 Disable cache来保证拿到最新的资源。
1)当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存。
2)当f5刷新网页时,跳过强缓存,但是会检查协商缓存。
浏览器缓存加载流程图:
另外 浏览器对资源的加载有优先级,可以针对这块进行一个网站的优化。