对于浏览器缓存,相信不少开发者对它真的是又爱又恨。一方面极大地提高了用户体验,而另外一方面有时会由于读取了缓存而展现了“错误”的东西,而在开发过程当中想方设法地想把缓存禁掉。那么浏览器缓存到底是个什么样的神奇玩意呢?html
简单来讲,浏览器缓存就是把一个已经请求过的Web资源(如html页面,图片,js,数据等)拷贝一份副本储存在浏览器中。缓存会根据进来的请求保存输出内容的副本。当下一个请求来到的时候,若是是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,仍是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,若是网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页。至于浏览器和网站服务器是如何标识网站页面是否更新的机制,将在后面介绍。node
上图就是使用了缓存的栗子,在页面请求以后,web资源都被缓存了,在后面的重复请求中,能够看到许多资源都是直接从缓存中读取的(from cache),而不是从新去向服务器请求。web
(1)减小网络带宽消耗浏览器
不管对于网站运营者或者用户,带宽都表明着金钱,过多的带宽消耗,只会便宜了网络运营商。当Web缓存副本被使用时,只会产生极小的网络流量,能够有效的下降运营成本。缓存
(2)下降服务器压力安全
给网络资源设定有效期以后,用户能够重复使用本地的缓存,减小对源服务器的请求,间接下降服务器的压力。同时,搜索引擎的爬虫机器人也能根据过时机制下降爬取的频率,也能有效下降服务器的压力。服务器
(3)减小网络延迟,加快页面打开速度网络
带宽对于我的网站运营者来讲是十分重要,而对于大型的互联网公司来讲,可能有时由于钱多而真的不在意。那Web缓存还有做用吗?答案是确定的,对于最终用户,缓存的使用可以明显加快页面打开速度,达到更好的体验。负载均衡
对于浏览器端的缓存来说,这些规则是在HTTP协议头和HTML页面的Meta标签中定义的。他们分别从新鲜度和校验值两个维度来规定浏览器是否能够直接使用缓存中的副本,仍是须要去源服务器获取更新的版本。分布式
新鲜度(过时机制):也就是缓存副本有效期。一个缓存副本必须知足如下条件,浏览器会认为它是有效的,足够新的:
1. 含有完整的过时时间控制头信息(HTTP协议报头),而且仍在有效期内;
2. 浏览器已经使用过这个缓存副本,而且在一个会话中已经检查过新鲜度;
知足以上两个状况的一种,浏览器会直接从缓存中获取副本并渲染。
校验值(验证机制):服务器返回资源的时候有时在控制头信息带上这个资源的实体标签Etag(Entity Tag),它能够用来做为浏览器再次请求过程的校验标识。如过发现校验标识不匹配,说明资源已经被修改或过时,浏览器需求从新获取资源内容。
(1)使用HTML Meta 标签
Web开发者能够在HTML页面的<head>节点中加入<meta>标签,代码以下
<meta http-equiv="Pragma" content="no-cache">
<!- Pragma是http1.0版本中给客户端设定缓存方式之一,具体做用会在后面详细介绍 -->
上述代码的做用是告诉浏览器当前页面不被缓存,每次访问都须要去服务器拉取。可是!这里有个坑...
事实上这种禁用缓存的形式用处颇有限:
a. 仅有IE才能识别这段meta标签含义,其它主流浏览器仅识别“Cache-Control: no-store”的meta标签。
b. 在IE中识别到该meta标签含义,并不必定会在请求字段加上Pragma,但的确会让当前页面每次都发新请求(仅限页面,页面上的资源则不受影响)。
(2)使用缓存有关的HTTP消息报头
在这里就须要先跟你们介绍一下HTTP的相关知识。一个URI的完整HTTP协议交互过程是由HTTP请求和HTTP响应组成的。有关HTTP详细内容可参考《Hypertext Transfer Protocol — HTTP/1.1》、《HTTP协议详解》等。
在HTTP请求和响应的消息报头中,常见的与缓存有关的消息报头有:
规则 | 消息包头 | 值/示例 | 类型 | 做用 |
新鲜度 | Pragma | no-cache | 响应 | 告诉浏览器忽略资源的缓存副本,每次访问都须要去服务器拉取【http1.0中存在的字段,在http1.1已被抛弃,使用Cache-Control替代,但为了作http协议的向下兼容,不少网站依旧会带上这个字段】 |
Expires | Mon, 15 Aug 2016 03:56:47 GMT | 响应 | 启用缓存和定义缓存时间。告诉浏览器资源缓存过时时间,若是还没过该时间点则不发请求【http1.0中存在的字段,该字段所定义的缓存时间是相对服务器上的时间而言的,若是客户端上的时间跟服务器上的时间不一致(特别是用户修改了本身电脑的系统时间),那缓存时间可能就没啥意义了。在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代】 | |
Cache-Control | no-cache | 响应 | 告诉浏览器忽略资源的缓存副本,强制每次请求直接发送给服务器,拉取资源,但不是“不缓存” | |
no-store | 响应 | 强制缓存在任何状况下都不要保留任何副本
|
||
max-age=[秒] | 响应 | 指明缓存副本的有效时长,从请求时间开始到过时时间之间的秒数 | ||
public | 响应 | 任何路径的缓存者(本地缓存、代理服务器),能够无条件的缓存改资源 | ||
private | 响应 | 只针对单个用户或者实体(不一样用户、窗口)缓存资源 | ||
Last-Modified | Mon, 15 Aug 2016 03:56:47 GMT | 响应 | 告诉浏览器这个资源最后的修改时间。服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一块儿返回给客户端【只 能精确到秒级,若是某些文件在1秒钟之内,被修改屡次的话,它将不能准确标注文件 的修改时间
|
|
If-Modified-Since | Mon, 15 Aug 2016 03:56:47 GMT | 请求 | 其值为上次响应头的Last-Modified值,再次向web服务器请求时带上头If-Modified-Since。web服务器收到请求后发现有头If-Modified-Since则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),包括更新Last-Modified的值,HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304(无需包体,节省浏览),告知浏览器继续使用所保存的cache | |
校验值 | ETag | "fd56273325a2114818df4f29a628226d" | 响应 | 告诉浏览器当前资源在服务器的惟一标识符(生成规则又服务器决定) |
If-None-Match | "fd56273325a2114818df4f29a628226d" | 请求 | 当资源过时时(使用Cache-Control标识的max-age),发现资源具备Etage声明,则再次向web服务器请求时带上头If-None-Match(Etag的值)。web服务器收到请求后发现有头If-None-Match则与被请求资源的相应校验串进行比对,决定返回200或304 |
在咱们对HTTP请求头和响应头的部分字段有了必定的认识以后,咱们接下来就来讨论不一样字段之间的关系和区别:
· Cache-Control与Expires
Cache-Control与Expires的做用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据仍是从新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,若是同时设置的话,其优先级高于Expires。
· Last-Modified/ETag与Cache-Control/Expires
配置Last-Modified/ETag的状况下,浏览器再次访问统一URI的资源,仍是会发送请求到服务器询问文件是否已经修改,若是没有,服务器会只发送一个304回给浏览器,告诉浏览器直接从本身本地的缓存取数据;若是修改过那就整个数据从新发给浏览器;
Cache-Control/Expires则不一样,若是检测到本地的缓存仍是有效的时间范围内,浏览器直接使用本地副本,不会发送任何请求。二者一块儿使用时,Cache-Control/Expires的优先级要高于Last-Modified/ETag。即当本地副本根据Cache-Control/Expires发现还在有效期内时,则不会再次发送请求去服务器询问修改时间(Last-Modified)或实体标识(Etag)了。
通常状况下,使用Cache-Control/Expires会配合Last-Modified/ETag一块儿使用,由于即便服务器设置缓存时间, 当用户点击“刷新”按钮时,浏览器会忽略缓存继续向服务器发送请求,这时Last-Modified/ETag将可以很好利用304,从而减小响应开销。
· Last-Modified与ETag
你可能会以为使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为何还须要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:
Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的惟一标识符,可以更加准确的控制缓存。Last-Modified与ETag是能够一块儿使用的,服务器会优先验证ETag,一致的状况下,才会继续比对Last-Modified,最后才决定是否返回304。Etag的服务器生成规则和强弱Etag的相关内容能够参考,《互动百科-Etag》和《HTTP Header definition》,这里再也不深刻。
注意:
1. Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的惟一标识符,可以更加准确的控制缓存,可是须要注意的是分布式系统里多台机器间文件的last-modified必须保持一致,以避免负载均衡到不一样机器致使比对失败,Yahoo建议分布式系统尽可能关闭掉Etag(每台机器生成的etag都会不同,由于除了 last-modified、inode 也很难保持一致)。
2. Last-Modified/If-Modified-Since要配合Cache-Control使用,Etag/If-None-Match也要配合Cache-Control使用。
第一次请求:
再次请求:
浏览器缓存行为还有用户的行为有关,具体状况以下:
用户操做 | Expires/Cache-Control | Last-Modified/Etag |
地址栏回车 | 有效 | 有效 |
页面连接跳转 | 有效 | 有效 |
新开窗口 | 有效 | 有效 |
前进、后退 | 有效 | 有效 |
F5刷新 | 无效(BR重置max-age=0) | 有效 |
Ctrl+F5刷新 | 无效(重置Cache-Control=no-cache) | 无效(请求头丢弃该选项 |
固然并非全部请求都能被缓存,没法被浏览器缓存的请求以下:
1. HTTP信息头中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告诉浏览器不用缓存的请求
2. 须要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的
3. 通过HTTPS安全加密的请求(有人也通过测试发现,ie其实在头部加入Cache-Control:max-age信息,firefox在头部加入Cache-Control:Public以后,可以对HTTPS的资源进行缓存,参考《HTTPS的七个误解》)
4. POST请求没法被缓存
5. HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求没法被缓存
参考资料:
1. http://www.cnblogs.com/520yang/articles/4807408.html 浏览器 HTTP 协议缓存机制详解
2. https://my.oschina.net/leejun2005/blog/369148 浏览器 HTTP 协议缓存机制详解
3. http://web.jobbole.com/82997/ 浏览器缓存机制浅析
4. http://www.alloyteam.com/2012/03/web-cache-2-browser-cache/ Web浏览器的缓存机制
5. http://www.cnblogs.com/vajoy/p/5341664.html 浅谈浏览器http的缓存机制
6. http://mp.weixin.qq.com/s/yf0pWRFM7v9Ru3D9_JhGPQ 浏览器缓存机制剖析