原文连接: https://github.com/yiliang114...
这里说的缓存是指浏览器(客户端)在本地磁盘中对访问过的资源保存的副本文件。css
浏览器缓存主要有如下几个优势:html
浏览器缓存分为强缓存和协商缓存,二者有两个比较明显的区别:nginx
chrome
中强缓存(虽然没有发出真实的 http
请求)的请求状态码返回是 200 (from cache)
;而协商缓存若是命中走缓存的话,请求的状态码是 304 (not modified)
。 不一样浏览器的策略不一样,在 Fire Fox
中,from cache
状态码是 304.其中 from cache 会分为 from disk cache 和 from memory cache. 从内存中获取最快,可是是 session 级别的缓存,关闭浏览器以后就没有了。
![]()
浏览器在第一次请求后缓存资源,再次请求时,会进行下面两个步骤:git
header
中的信息,根据 response header
中的 expires
和 cache-control
来判断是否命中强缓存,若是命中则直接从缓存中获取资源。IF-Modified-Since
或者 IF-None-Match
, 它们的值分别是第一次请求返回 Last-Modified
或者 Etag
,由服务器来对比这一对字段来判断是否命中。若是命中,则服务器返回 304 状态码,而且不会返回资源内容,浏览器会直接从缓存获取;不然服务器最终会返回资源的实际内容,并更新 header 中的相关缓存字段。借用网上的一张图片github
强缓存是根据返回头中的 Expires
或者 Cache-Control
两个字段来控制的,都是表示资源的缓存有效时间。chrome
Expires
是 http 1.0
的规范,值是一个GMT
格式的时间点字符串,好比 Expires:Mon,18 Oct 2066 23:59:59 GMT
。这个时间点表明资源失效的时间,若是当前的时间戳在这个时间以前,则断定命中缓存。有一个缺点是,失效时间是一个绝对时间,若是服务器时间与客户端时间误差较大时,就会致使缓存混乱。而服务器的时间跟用户的实际时间是不同是很正常的,因此 Expires
在实际使用中会带来一些麻烦。Cache-Control
这个字段是 http 1.1
的规范,通常经常使用该字段的 max-age
值来进行判断,它是一个相对时间,好比 .Cache-Control:max-age=3600
表明资源的有效期是 3600 秒。而且返回头中的 Date
表示消息发送的时间,表示当前资源在 Date ~ Date +3600s
这段时间里都是有效的。不过我在实际使用中经常遇到设置了 max-age
以后,在 max-age
时间内从新访问资源却会返回 304 not modified
,这是因为服务器的时间与本地的时间不一样形成的。固然 Cache-Control
还有其余几个值能够设置, 不过相对来讲都不多用了:后端
no-cache
不使用本地缓存。须要使用协商缓存。no-store
直接禁止浏览器缓存数据,每次请求资源都会向服务器要完整的资源, 相似于 network
中的 disabled cache
。public
能够被全部用户缓存,包括终端用户和 cdn 等中间件代理服务器。private
只能被终端用户的浏览器缓存。若是 Cache-Control
与 Expires
同时存在的话, Cache-Control
的优先级高于 Expires
。浏览器
协商缓存是由服务器来肯定缓存资源是否可用。 主要涉及到两对属性字段,都是成对出现的,即第一次请求的响应头带上某个字, Last-Modified
或者 Etag
,则后续请求则会带上对应的请求字段 If-Modified-Since
或者 If-None-Match
,若响应头没有 Last-Modified
或者 Etag
字段,则请求头也不会有对应的字段。缓存
Last-Modified/If-Modified-Since
两者的值都是 GMT 格式的时间字符串, Last-Modified
标记最后文件修改时间, 下一次请求时,请求头中会带上 If-Modified-Since
值就是 Last-Modified
告诉服务器我本地缓存的文件最后修改的时间,在服务器上根据文件的最后修改时间判断资源是否有变化, 若是文件没有变动则返回 304 Not Modified
,请求不会返回资源内容,浏览器直接使用本地缓存。当服务器返回 304 Not Modified
的响应时,response header
中不会再添加的 Last-Modified
去试图更新本地缓存的 Last-Modified
, 由于既然资源没有变化,那么 Last-Modified
也就不会改变;若是资源有变化,就正常返回返回资源内容,新的 Last-Modified
会在 response header
返回,并在下次请求以前更新本地缓存的 Last-Modified
,下次请求时,If-Modified-Since
会启用更新后的 Last-Modified
。Etag/If-None-Match
, 值都是由服务器为每个资源生成的惟一标识串,只要资源有变化就这个值就会改变。服务器根据文件自己算出一个哈希值并经过 ETag
字段返回给浏览器,接收到 If-None-Match
字段之后,服务器经过比较二者是否一致来断定文件内容是否被改变。与 Last-Modified
不同的是,当服务器返回 304 Not Modified
的响应时,因为在服务器上ETag
从新计算过,response header
中还会把这个 ETag
返回,即便这个 ETag
跟以前的没有变化。HTTP 中并无指定如何生成 ETag,能够由开发者自行生成,哈希是比较理想的选择。
HTTP1.1
中 Etag
的出现主要是为了解决几个 Last-Modified
比较难解决的问题:服务器
If-Modified-Since
能检查到的粒度是秒级的,使用 Etag
就可以保证这种需求下客户端在 1 秒内能刷新 N 次 cache。Cache-Control > expires > Etag > Last-Modified
简单说就是 F5 刷新的时候,会暂时禁用强缓存
通过对 qq、fire fox 、safari 、chrome 这几个浏览器的访问同一个页面测试我发现,不一样的浏览器在 F5 刷新的时候 ,同一个文件 qq 、fire fox 浏览器会返回 304 Not Nodified
,在请求头中不携带 Expires/Cache-Control
; 而 chrome 和 safari 刷新的时候,会返回 200 from cache
, 没有真正发起请求,走强缓存。可见不一样的浏览器反馈是不一致的,因此下面表格中"F5 刷新"时 Expires/Cache-Control
会无效我认为是存在必定争议的。
而 Ctrl + F5 强制刷新的时候,会暂时禁用强缓存和协商缓存。
在写这篇博客时,对于我仅仅测试了一个浏览器以后便写了无效(由于网上大多数帖子写了无效,我也觉得我验证经过了),对指出这个问题的群友,表示感谢,但愿其余人不会被我误导。
用户操做 | Expires/Cache-Control | Last-Modied/Etag |
---|---|---|
地址栏回车 | 有效 | 有效 |
页面连接跳转 | 有效 | 有效 |
新开窗口 | 有效 | 有效 |
前进回退 | 有效 | 有效 |
F5 刷新 | 无效(有争议,不一样浏览器反馈不一致) | 有效 |
Ctrl+F5 强制刷新 | 无效 | 无效 |
后端服务器,写入代码逻辑中:
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"
通常来讲,经过 nginx 静态资源服务器,会默认给资源带上强缓存、协商缓存的 header 字段。
cache-control
定义的 max-age
时间以内,js
, css
文件会走强缓存,http
状态码是 200, 跟服务器也并不会有交互。可是第一个文件 index.html
文件, 每次回车或者刷新都是状态码都是 304 ,由于它的请求头中默认每次都携带了 Cache-Control: max-age=0
。js
css
文件 cache-control
超时以后,从新按回车会走协商缓存,请求服务器发现资源没有改变,因而返回 304 ,浏览器从缓存中获取内容,从 size
中也能够看出端倪, 几百 B 的包不是静态资源的体积。最后总结一下浏览器的三级缓存原理: