cache-control
的 public
、private
你知道何时使用吗?ETag
可能也有不适用的时候吗?Vary
和缓存有怎样的关系吗?若是上面的问题你都懂了,那关掉这个页面吧;若是你喜欢本文,点一个赞吧~css
先小小的回顾下html
http header | 描述 | 强缓存 | 协商缓存 |
---|---|---|---|
Pragma |
老版本的本地缓存机制,http 1.0 及如下 | ||
Expires |
在此时候以后,响应过时,时间是绝对时间,受本地时间影响 | * | |
Cache-Control |
强缓存策略 Cache-Control: public, max-age=31536000, must-revalidate max-age 是相对时间 |
* | |
Last-Modified 、If-Modified-Since |
资源最后被更改的时间,精确到秒 | * | |
ETag 、If-None-Match |
资源的标识值,用来惟一的标识一个资源 | * |
处理优先级git
在本地 Cache-Control
> Expires
,Pragma
在不支持 Cache-Control
时生效。github
若是本地缓存过时,则要依靠协商缓存算法
ETag
> Last-Modified
浏览器
强缓存的 http 状态码是 200 OK缓存
协商缓存的 http 状态码是 304 Not Modifiedbash
public
代表响应能够被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存。private
代表响应只能被单个用户缓存,不能做为共享缓存(即代理服务器不能缓存它)。私有缓存能够缓存响应内容。no-cache
即便有缓存也会向服务器发请求。no-store
让客户端不要把资源存在缓存。max-age=<seconds>
设置缓存存储的最大周期,超过这个时间缓存被认为过时(单位秒)。与 Expires
相反,时间是相对于请求的时间。s-maxage=<seconds>
覆盖 max-age
或者 Expires
头,可是仅适用于共享缓存(好比各个代理),私有缓存会忽略问题 关于 public
和 private
的区别 服务器
翻译成中文:private
不容许代理缓存。举个例子,ISP 服务商能够在你的客户端和互联网之间加上不可见的代理,这个代理会缓存网页来下降带宽,客户端设置 cache-control: private
以后,能够指定 ISP 代理不容许缓存网页,可是容许最后的接受者缓存。而使用 cache-control: public
的意思是说,谁均可以缓存哈,因此中间代理会缓存一份以减小带宽下降费用。网络
若是是谁均可以访问的内容,好比网站 logo,那就用 public
好了,反正也不会有关键数据泄露风险,尽可能中间代理都给缓存上,减小带宽。而若是是一个含有用户信息的页面,好比页面含有个人用户名,那这个页面固然不是对谁都有用,由于不一样的人要返回不一样的用户名嘛,这个时候 private
会适合一些,若是代理缓存了个人这个页面,别的用户访问又会缓存别人的,这显然不合理,并且你的我的私密数据也尽可能不要被保存在不受信任的地方。
固然,全部不被表示为 public
的数据都应该被标识为 private
,要否则数据会存储在中间服务器上,别人就有可能会访问到这个数据。
禁止缓存
Cache-Control: no-cache, no-store, must-revalidate
缓存静态资源
Cache-Control:public, max-age=86400
ETag
和 If-None-Match
常被用来处理协商缓存。而 ETag
和 If-Match
能够 避免“空中碰撞”。
ETag
HTTP响应头是资源的特定版本的标识符。这可让缓存更高效,并节省带宽,由于若是内容没有改变,Web服务器不须要发送完整的响应。而若是内容发生了变化,使用 ETag
有助于防止资源的同时更新相互覆盖(“空中碰撞”)。
当编辑 MDN 时,当前的 Wiki 内容被散列,并在响应中放入Etag
:
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4 复制代码
将更改保存到 Wiki 页面(发布数据)时,POST 请求将包含有 ETag
值的 If-Match
头来检查是否为最新版本。
If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
复制代码
若是哈希值不匹配,则意味着文档已经被编辑,抛出 412 ( Precondition Failed) 前提条件失败错误。
If-None-Match
是客户端发送给服务器时的请求头,其值是服务器返回给客户端的 ETag
,当 If-None-Match
和服务器资源最新的 Etag
不一样时,返回最新的资源及其 Etag
。
Last-Modified
、If-Modified-Since
是资源最后更改的时间。
Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
复制代码
这两个的区别是: Last-Modified
是服务器发送给客户端的,If-Modified-Since
是客户端发送给服务器的。
问题 Last-Modified
机制和 ETag
机制的区别和优先级是怎样的?
Last-Modified
只能精确到秒,因此在秒级如下的更改没法检测到。而 ETag
能够表征文件的任何更改,只要文件变化 ETag
就会变化。因此 Last-Modified
是一个备用机制,优先级不如 Etag
。
客户端请求带有 If-None-Match
在服务端校验匹配则返回 304,校验不匹配则返回 200,同时返回最新的资源和 Etag
。
Age
消息头里包含消息对象在缓存代理中存贮的时长,以秒为单位。
Age
消息头的值一般接近于0。表示此消息对象刚刚从原始服务器获取不久;其余的值则是表示代理服务器当前的系统时间与此应答消息中的通用消息头 Date 的值之差。
Age: <delta-seconds>
复制代码
如
age: 1135860
复制代码
Date
是一个通用首部,其中包含了报文建立的日期和时间。
指的是响应生成的时间. 请求通过代理服务器时, 返回的 Date
未必是最新的, 一般这个时候, 代理服务器将增长一个 Age
字段告知该资源已缓存了多久.
Date: Wed, 21 Oct 2015 07:28:00 GMT
复制代码
Vary
是一个HTTP响应头部信息,它决定了对于将来的一个请求头,应该用一个缓存的回复(response)仍是向源服务器请求一个新的回复。它被服务器用来代表在 content negotiation algorithm(内容协商算法)中选择一个资源表明的时候应该使用哪些头部信息(headers)。
对于服务器而言, 资源文件可能不止一个版本, 好比说压缩和未压缩, 针对不一样的客户端, 一般须要返回不一样的资源版本。 好比说老式的浏览器可能不支持解压缩, 这个时候, 就须要返回一个未压缩的版本; 对于新的浏览器, 支持压缩, 返回一个压缩的版本, 有利于节省带宽, 提高体验. 那么怎么区分这个版本呢, 这个时候就须要 Vary
了。
服务器经过指定 Vary: Accept-Encoding
, 告知代理服务器, 对于这个资源, 须要缓存两个版本: 压缩和未压缩. 这样老式浏览器和新的浏览器, 经过代理, 就分别拿到了未压缩和压缩版本的资源, 避免了都拿同一个资源的尴尬。
Vary: Accept-Encoding,User-Agent
复制代码
如上设置, 代理服务器将针对是否压缩和浏览器类型两个维度去缓存资源. 如此一来, 同一个url, 就能针对 PC 和 Mobile 返回不一样的缓存内容。
能够设置 Cache-Control
Cache-Control: no-cache, no-store, must-revalidate
复制代码
也能够给资源增长版本号,这样能够很方便地控制何时加载最新资源,这也是目前作版本更新比较经常使用的手段,即便老资源还在有效期内,加上了 query、hash。
<link rel="stylesheet" type="text/css" href="../css/style.css?version=1.8.9"/>
复制代码
用户按 f5(ctrl+r)、ctrl+f五、点击前进后退 都会触发缓存机制
通过本地测试发现和网上传的有些出入,记录以下(强缓存有效表明直接使用本地的缓存文件,Chrome 状态码为 200,协商缓存有效表明向服务器发起请求,服务器可能返回 304 无内容或者 200 有内容)。
操做 | 强缓存 | 协商缓存 |
---|---|---|
页面连接跳转 | 有效 | 有效 |
新开窗口 | 有效 | 有效 |
前进后退 | 有效 | 有效 |
地址栏回车 | 失效 或者 有效 | 有效 |
ctrl+r 或者 f5 |
失效 | 有效 |
ctrl+f5 强制刷新 |
失效 | 失效 |
地址栏回车和网络上不同,打个比方,若是当前已经在 http://localhost:3333/
,而后在地址栏选中后回车,你会发现没有缓存。
可是若是当前不在 http://localhost:3333/
,好比 http://localhost:3333/index.css
或者空白页,而后输入 http://localhost:3333/
回车,这时候就会直接从本地缓存中读取。
惊喜不,意外不
这两种缓存类型存在于 Chrome 中。
上个小标题 用户行为与缓存,咱们看到浏览器从本地读缓存的时候有一个 disk cache,与之对应的还有一个 memory cache。看名字也能大概才出来,disk cache 是从硬盘中读取的文件缓存,memory cache 是从内存中直接读取的内容,速度上固然也是后者更快。
那为何要有这两种缓存的形式呢?
disk cache 存在硬盘,能够存不少,容量上限比内容缓存高不少,而 memory cache 从内存直接读取,速度上占优点,这两个各有各的好处!
问题 浏览器如何决策使用哪一种缓存呢?
来自知乎 浏览器是根据什么决定「from disk cache」与「from memory cache」?
这个内容网络上不多有文章介绍,通过我测试以后发现区别挺大的。Chome 的缓存处理和另外两个存在明显的不一样!
上面讲的强缓存、memory cache、disk cache 在 FF、IE 中都没有明显的区分,或许这就是 Chrome 速度快的缘由?
咱们举个例子,屡次重复请求 https://www.baidu.com/
,查看三个浏览器的区别。
Chrome
FF
IE
Chrome 中有强缓存、协商缓存,强缓存分为 memory cache、disk cache,这种处理机制能够最大化的利用缓存,减小发起的请求,从而优化页面加载速度!!Chrome 资源状态码都是 200,没有 304,另外两家都存在大量304,在 FF、IE 中,即便服务器明确表示资源须要进行强缓存,好比 Cache-Control: max-age=86400
,他们仍然不会应用所谓的强缓存策略,仍然会向服务器发送请求,服务器返回 304 ,告诉浏览器用你本地的资源就行了!!!
怎么知道这个请求确实是发送到了代理或者真实服务器呢?咱们上面有说 Age
和 Date
,Age 表示当前请求在返回时,在代理那里的时间减去 Date
的时间,因此每次只要请求发出去了,Age
都会相比上一次增长!!!
咱们以掘金的 https://b-gold-cdn.xitu.io/v3/static/js/0.81b469df7a0dd87832a4.js
文件为例。在 FF 上,前一次的结果是
刷一下
Date
没有变,表示都是使用的同一个真实服务器的响应资源,Age
后一次比前一次变大了,可是状态码都是 304,而缓存规则是 cache-control: s-maxage=2592199, max-age=2592199
!!!
因此想要作好缓存,须要考虑浏览器兼容的问题,综合使用 http headers。
这个问题大多数讲缓存的文章也没有说起。
Etag
是经过资源内容生成的,因此会有一个计算成本存在,本如大图片的更改,它的最后更改时间能够很容易得到,可是计算 Etag
成本就会高不少了。
参考
喜欢的话,给个点赞吧~
欢迎你们关注个人掘金和公众号,算法、TypeScript、React 及其生态源码按期讲解。