浏览器缓存就是把一个已经请求过的Web资源(如html页面,图片,js,数据等)拷贝一份副本储存在浏览器中。缓存会根据进来的请求保存输出内容的副本。当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。
浏览器缓存(BrowserCaching)是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的显示。
浏览器缓存优点:
①.减少网络带宽消耗
无论对于网站运营者或者用户,带宽都代表着金钱,过多的带宽消耗,只会便宜了网络运营商。当Web缓存副本被使用时,只会产生极小的网络流量,可以有效的降低运营成本。
②.降低服务器压力
给网络资源设定有效期之后,用户可以重复使用本地的缓存,减少对源服务器的请求,间接降低服务器的压力。同时,搜索引擎的爬虫机器人也能根据过期机制降低爬取的频率,也能有效降低服务器的压力。
③.减少网络延迟,加快页面打开速度
带宽对于个人网站运营者来说是十分重要,而对于大型的互联网公司来说,可能有时因为钱多而真的不在乎。那Web缓存还有作用吗?答案是肯定的,对于最终用户,缓存的使用能够明显加快页面打开速度,达到更好的体验。
浏览器缓存缺点:
在项目开发阶段因为缓存的原因导致资源没有及时更新而看不到最新的效果。
浏览器缓存又分为:强缓存和协商缓存两种。
http1.0 的 expires
http1.0 中,通过头部字段 expires 定义缓存有效时间。expires 是一个绝对的日期。那么这个绝对值是取决于服务器,如果服务器和客户端的时间并不一致,那么就可能导致缓存过早失效或客户端使用已经过期的缓存。
http1.1 的 Cache-Control
为了解决这个问题,在http1.1中引入了Cache-Control字段,是最重要的规则,可以通过 max-age(单位为秒) 主要用于控制网页缓存,主要取值为:
Cache-Control 的值配置如下
请求头
no-cache: 使用本地缓存必须向服务器验证
no-store: 不缓存该请求的任何内容
max-age: 响应的最大有效时间
max-stale: 接受过期多久的缓存
min-fresh: 期望在指定时间内的响应仍然有效
no-transform: 代理不可更改媒体类型
only-if-cached: 从缓存获取资源
cache-extension:新指令的标记
响应头
public: 任何机器都可以缓存。
private: 只允许特定的人缓存。
no-cache:要求客户端使用缓存时,必须向服务器确认。
no-store:不允许任何缓存
no-transform:代理不能更改媒体类型
must-revalidate:可以缓存但是必须向服务器确认
proxy-revalidate:中间代理必须对响应缓存的有效性再次确认
max-age:最大缓存有效时间
s-maxage:中间服务器最大缓存时间
cache-extension:新指令标记
Cache-Control缓存原理
①.浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Cache-Control
②.浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来
③.浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,根据它第一次的请求时间和Cache-Control设定的有效期,计算出一个资源过期时间,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则就不行
④.如果缓存没有命中,浏览器直接从服务器加载资源时,Cache-Control Header在重新加载的时候会被更新
强缓存过期后,进入了协商缓存阶段。这个阶段目的在于,判断本地缓存是否还有效。如果有效则服务器会通知浏览器使用本地缓存。
通常,我们实现协商缓存有两种方式。
Last-Modified / IF-Modified-Since
通过 Last-modified/if-Modified-Since
实现
当服务响应请求时,会将 Last-Modified 设置在响应头中,浏览器会记录该字段。
当该文件过期后,浏览器会将第一次收到的 Last-Modified 的值,设置到 请求头的 If-Modified-Since 字段中。
服务器收到请求,取出 If-Modified-Since 字段,与文件修改时间对比,如果没有改变就 返回 304(not modified),并更新浏览器缓存资源有效期。如果,不相同则返回该请求的资源。
缺点:只能判断秒级的改变,当变化文件在1秒以内,浏览器会认为缓存命中返回 304。为了解决这个问题,HTTP1.1 引入 ETag
Etag / IF-None-Match
ETag 是通过 hash 算法,计算出来的文件唯一标识。当文件改变其计算出来的hash值也将是不同的。
当浏览器第一请求资源时,服务器会计算出文件的 hash 值,并将得到值 设置到响应头的 ETag 上。
浏览器接收响应,保存 ETag。当浏览缓存过期时,就将保存的 ETag 值,设置在 请求头的 If-None-Match 上。
服务器接收该请求,取出If-None-Match 值,并和服务器保存的文件进行 hash 比对。如果一样则 返回 304,并更新过期时间。不一样则返回新的文件。
缺点:
hash 值的计算需要服务器的处理,如果用户量巨大。通过 ETag 进行协商缓存,对性能的浪费是巨大的。ETag 的出现并不是要代替 Last-Modified 的。
为什么要有 Etag
HTTP1.1 中 Etag 的出现主要是为了解决几个 Last-Modified 比较难解决的问题:
一些文件也许会周期性的更改,但是内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新 GET;
某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说 1s 内修改了 N 次),If-Modified-Since
能检查到的粒度是秒级的,使用 Etag
就能够保证这种需求下客户端在 1 秒内能刷新 N 次 cache。
某些服务器不能精确的得到文件的最后修改时间。
协商缓存总结如下:
请求资源时,把用户本地该资源的 etag 同时带到服务端,服务端和最新资源做对比。
如果资源没更改,返回304,浏览器读取本地缓存。
如果资源有更改,返回200,返回最新的资源。
当然并不是所有请求都能被缓存,无法被浏览器缓存的请求如下:
①.HTTP信息头中包含Cache-Control:no-cache,pragma:no-cache,或Cache-Control:max-age=0等告诉浏览器不用缓存的请求;
②.需要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的;
③.经过HTTPS安全加密的请求;
④.POST请求无法被缓存;
⑤.HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求无法被缓存;
用户行为对缓存的影响
用户操作 | Expires/Cache-Control | Last-Modied/Etag |
地址栏回车 | 有效 | 有效 |
页面链接跳转 | 有效 | 有效 |
新开窗口 | 有效 | 有效 |
前进回退 | 有效 | 有效 |
F5 刷新 | 效(有争议,不同浏览器反馈不一致) | 有效 |
Ctrl+F5 强制刷新 | 无效 | 无效 |
后端服务器如nodejs
res.setHeader('max-age': '3600 public') res.setHeader(etag: '5c20abbd-e2e8') res.setHeader('last-modified': Mon, 24 Dec 2018 09:49:49 GMT)
Nginx 配置
add_header Cache-Control "max-age=3600"
如果觉得博客文章对您有帮助,异或土豪有钱任性,可以通过以下扫码向我捐助。也可以动动手指,帮我分享和传播。您的肯定,是我不懈努力的动力!感谢各位亲~