阅读621 返回首页    go 阿里云 go 技术社区[云栖]


浏览器缓存机制

分类

浏览器的缓存主要包括两种缓存:强缓存、验证缓存。
强缓存是指浏览器不与服务器进行任何交互请求,直接将浏览器的缓存数据(包括缓存数据的response头信息)返回给用户。这种缓存给用户的响应是最快的,但同时也是风险性较高的。因为该类缓存没有进行任何的校验即直接反馈给用户,是可能存在有历史的脏数据。可以从以下两个方面查看到该次请求是强缓存:
1)浏览器返回200 (From Cache):
image
2)Response头中的Date字段所表示的时间小于当前时间(Date字段表示的是源站服务器第一次产生响应数据的时间):
image
强缓存所依赖的header头包括:Cache-Control:max-age和Expires头。Cache-Control头和Expires头都是都是缓存数据的有效期的信息。只不过HTTP/1.0+的Expires头是采用的绝对GMT时间,而HTTP/1.1的Cache-Control:max-age则是采用的相对时间进行存储。在实际使用中我们更倾向于使用Cache-Control:max-age头,因为Expires头记录的是服务器端设置的绝对时间,如果客户端与服务器之间的时间差别较大的话可能会导致有偏差;并且当Cache-Control:max-age和Expires头同时存在的情况下,Cache-Control头将覆盖Expires头。当请求发起的时间仍然在Cache-Control或者Expires设置的有效期内的话则将直接读取缓存数据。
验证缓存(又叫协商缓存)是指浏览器根据缓存资源的Last-Modified字段和Etag字段得到If-Modified-Since和If-None-Match字段加入request头中向服务器进行验证源站服务器的资源是否有更新过,如果发现源站资源没有进行变更即会返回304 Not Modified响应头。
image
下面分别介绍这几个字段的意义:
Last-Modified/If-Modified-Since:在客户端第一次向服务器端发起请求时,服务器返回数据并置状态码为200,同时将该文件最后修改的GMT时间记录在Last-Modified头中返回客户端。下次客户端请求验证缓存数据时就会将缓存数据中的Last-Modified字段记录为请求头中的If-Modified-Since字段向服务器端询问在该时间点后服务器的文件是否有做过更新,如果没有更新即返回304 Not Modified响应头并读取缓存数据,而如果服务器文件该时间点后更新过则需要重新将服务器的文件传输给客户端并返回200状态码,同时该文件的Last-Modified时间也将是服务器文件现在更新的时间。
image
Etag/If-None-Match:HTTP协议规格说明定义ETag为“被请求变量的实体值”。另一种说法是,ETag是一个可以与Web资源关联的记号(token)。HTTP/1.1并没有要求具体Etag中间需要存放什么内容或者实现方法,有一些ETag是通过文件资源的MD5值来进行标识的。与Last-Modified一样也是判断服务器文件是否有做过更新,其也是将上次缓存数据中的Etag记录为请求头中的If-None-Match头向服务器验证服务器文件的Etag是否更新过。有Etag验证主要解决以下几点Last-Modified无法解决的问题:
1、网站文件周期性更新但并不改变文件内容(仅修改Last-Modified时间),对于这些文件仍然希望可以使用缓存数据;
2、网站文件更新频率较快,小于秒级的更新频率通过Last-Modified无法识别;
3、服务器无法准确得到Last-Modified时间,需要Etag标识文件。
image

浏览器行为

浏览器有多种刷新行为可以影响下次请求对缓存数据的行为,并且不同的浏览器对于同样的刷新操作也有不同的情况。但是究其原因都是浏览器在发起请求的时候所带的catch-control的头信息来决定的。下面是HTTP/1.1文档中13.2.6 Disambiguating Multiple Responses的一段文档[1]:

When a client tries to revalidate a cache entry, and the response it receives contains a Date header that appears to be older than the one for the existing entry, then the client SHOULD repeat the request unconditionally, and include
Cache-Control: max-age=0
to force any intermediate caches to validate their copies directly with the origin server, or
Cache-Control: no-cache
to force any intermediate caches to obtain a new copy from the origin server.

从上面该文档可以知晓:如果一个client想忽略强缓存的数据而直接获取验证缓存的数据的话是需要在请求头中加上Cache-Control:max-age=0;而client如果想忽略强缓存和鉴权缓存,直接获取源服务器的内容而缓存数据全部重新从源站获取新的数据就需要在请求头中加上Cache-Control:no-cache的头。下面是在chrome下测试的结果图:
image
image
image

上面的测试分别是通过在地址栏回车重新键入地址、F5刷新以及Ctrl+F5刷新的测试结果。从上面的结果图中验证了上面的结论,并且其中对于Cache-Control: no-cache的测试中为了兼容HTTP/1.0加上了Pragma:no-cache的头信息,其作用于前者是一致的。因此不同的浏览器对于不同的刷新操作有不同的处理逻辑也是由request头中的Cache-Control头决定的。

流程总结

下面将根据两张图[2][3]总结浏览器缓存的处理逻辑。
(1)当浏览器向服务器端发送请求的时候首先查看查看本地浏览器缓存数据中是否有缓存数据,如果没有缓存数据则会向web服务器(这里的web服务器是广义的概念,有可能并不是源站服务器,有可能是CDN等缓存数据)请求对应的数据并将获取的响应数据以及一些对应的response头信息缓存到本地(包括Expires、Cache-Control等头信息),如果有缓存数据则执行(2);
image

(2)当本地有缓存数据并且request头中没有设置Cache-Control:no-cache和Cache-Control:max-age=0头信息的话就需要查看该缓存的Cache-Control头和Expires头查看该缓存数据是否新鲜,如果没有过期则直接读取强缓存数据返回给浏览器,返回状态码200(from cache)。如果有设置上述的两个Cache-Control头或者强缓存数据已经过期则执行(3);
(3)如果请求头中没有Cache-Control:no-cache头信息的话则客户端带着If-Modified-Since和If-None-Match参数向服务器发起验证,如果服务器端验证发现没有进行更新的话则直接返回304 Not Modified头和本地的缓存数据返回给客户端。如果请求头带了Cache-Control:no-cache或者源站做了更新则执行(4);
(4)如果请求头中有Cache-Control:no-cache或者验证缓存校验发现源站数据更新了,则需要从服务器重新获取数据并将其存入浏览器缓存并返回200状态码。
image

Reference

[1] Hypertext Transfer Protocol -- HTTP/1.1: https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
[2] HTTP 权威指南
[3] 缓存系列(1)——浏览器缓存协商: https://blog.csdn.net/chosen0ne/article/details/7344189
[4] http协商缓存VS强缓存: https://www.cnblogs.com/wonyun/p/5524617.html
[5] stackoverflow讨论帖: https://stackoverflow.com/questions/1046966/whats-the-difference-between-cache-control-max-age-0-and-no-cache

最后更新:2017-06-16 17:01:45

  上一篇:go  勒索软件横行,是该用key登陆ssh了!
  下一篇:go  移动医疗布局:借助互联网思维和渠道打通模式