浏览器缓存机制

浏览器缓存原理

浏览器向服务器首次发起请求后拿到请求结果,会根据响应报文中HTTP头的缓存标识,决定是否缓存结果。如果需要进行缓存,则将请求结果和缓存标识存入浏览器缓存中。

浏览器每次发起请求,都会在浏览器缓存中查找当前请求结果及缓存标识。

浏览器缓存策略

  • 浏览器在加载资源时,向浏览器缓存中查找请求结果。如果存在请求结果,则根据缓存标识expirescache-control来判断是否命中强缓存,是则直接从缓存读取资源,不会发请求到服务器。如果不存在请求结果,则发送请求到服务器。
  • 存在请求结果,但是不是强缓存或强缓存已失效,浏览器一定会发送一个请求到服务器,通过响应首部字段的last-modifiedetag验证资源是否命中协商缓存,如果是,服务器会将这个请求返回,但是不会返回这个资源的数据,依然是从缓存中读取资源。
  • 如果前面两者都没有命中,直接从服务器加载资源。

1

强缓存

当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的首部字段中和请求结果一起返回给浏览器,控制强制缓存的字段分别是ExpiresCache-Control,其中Cache-Control优先级比Expires高。

Expires

Expires 响应头包含日期/时间, 即在此时候之后,响应过期。(具体的日期时间)

Expires是HTTP/1.0控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。

到了HTTP/1.1,Expire已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比。但是客户端的时间随着时区的不同而不同,可以进行自定义客户端时间。所以这种情况下,Expire可能会直接过期。

如果在Cache-Control响应头设置了 “max-age” 或者 “s-max-age” 指令,那么 Expires 头会被忽略。

Cache-Control

Cache-Control 通用消息头字段,被用于在HTTP请求和响应中,通过指定指令来实现缓存机制。缓存指令是单向的,这意味着在请求中设置的指令,不一定被包含在响应中。

在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存,主要取值为:

  • public - 缓存响应指令 - 所有内容都可以被任何对象缓存(客户端和代理服务器都可缓存)
  • private - 缓存响应指令 - 所有内容只有客户端可以缓存,Cache-Control的默认取值(代理服务器不可缓存)
  • no-cache - 通用指令 - 客户端缓存内容,强制进行协商缓存来验证决定是否使用缓存内容(!!!注意
  • no-store - 通用指令 - 所有内容都不会被缓存,即不使用任何缓存
  • max-age=xxx (xxx is numeric) - 通用指令 - 缓存内容将在xxx秒后失效(相对时间)

Catch-Control的客户端请求指令和服务端响应指令部分不同,分为可缓存性、到期、重新验证和重新加载、其他等

no-cache名字具有误导性,其实是有缓存,但是必须要请求服务端,让服务端决定是否使用缓存内容(协商缓存)

协商缓存

浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存内容。

协商缓存的标识也是在响应报文的首部字段中和请求结果一起返回给浏览器的,控制协商缓存的字段分别有:Last-Modified / If-Modified-SinceETag / If-None-Match,其中ETag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。

Last-Modified / If-Modified-Since

Last-Modified 是一个响应首部,其中包含源头服务器认定的资源做出修改的日期及时间。 它通常被用作一个验证器来判断接收到的或者存储的资源是否彼此一致。由于精确度比 ETag 要低,所以这是一个备用机制。包含有 If-Modified-SinceIf-Unmodified-Since 首部的条件请求会使用这个字段。

If-Modified-Since 是一个条件式请求首部,服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 响应,而在 Last-Modified 首部中会带有上次修改时间。 不同于 If-Unmodified-Since, If-Modified-Since 只可以用在 GETHEAD 请求中。

当与 If-None-Match 一同出现时,它(If-Modified-Since)会被忽略掉,除非服务器不支持 If-None-Match

ETag / If-None-Match

ETagHTTP响应头是资源的特定版本的标识符。这可以让缓存更高效,并节省带宽,因为如果内容没有改变,Web服务器不需要发送完整的响应。而如果内容发生了变化,使用ETag有助于防止资源的同时更新相互覆盖(“空中碰撞”)。

资源变化会导致ETag变化,由服务器生成。 因此Etags类似于指纹,可以保证每一个资源是唯一的。

If-None-Match 是一个条件式请求首部。对于 GETGETHEAD 请求方法来说,当且仅当服务器上没有任何资源的 ETag 属性值与这个首部中列出的相匹配的时候,服务器端会才返回所请求的资源,响应码为 200 。对于其他方法来说,当且仅当最终确认没有已存在的资源的 ETag 属性值与这个首部中所列出的相匹配的时候,才会对请求进行相应的处理。

对于 GETHEAD 方法来说,当验证失败的时候,服务器端必须返回响应码 304 (Not Modified,未改变)。需要注意的是,服务器端在生成状态码为 304 的响应的时候,必须同时生成以下会存在于对应的 200 响应中的首部:Cache-Control、Content-Location、Date、ETag、Expires 和 Vary

ETag 属性之间的比较采用的是弱比较算法,即两个文件除了每个比特都相同外,内容一致也可以认为是相同的。例如,如果两个页面仅仅在页脚的生成时间有所不同,就可以认为二者是相同的。

当与 If-Modified-Since 一同使用的时候,If-None-Match 优先级更高。

请求首部 If-Match 的使用表示这是一个条件请求。在请求方法为 GETHEAD 的情况下,服务器仅在请求的资源满足此首部列出的 ETag值时才会返回资源。而对于 PUT 或其他非安全方法来说,只有在满足条件的情况下才可以将资源上传。

ETag 之间的比较使用的是强比较算法,即只有在每一个字节都相同的情况下,才可以认为两个文件是相同的。在 ETag 前面添加 W/ 前缀表示可以采用相对宽松的算法。

304状态

304_header

协商缓存优先级 :ETag / If-None-Match > Last-Modified / If-Modified-Since

使用缓存内容

304

不使用缓存内容,重新请求

200

两种机制的异同

相同点
  • 如果都命中,则是从浏览器缓存中加载资源,服务器不返回资源数据。
不同点
  • 强缓存优先于协商缓存
  • 强缓存不发送请求到服务器
  • 协商缓存会发送请求到服务器上进行询问

启发式缓存

参考链接:http://louiszhai.github.io/2017/04/07/http-cache/

如果Expires, Cache-Control: max-age, 或 Cache-Control:s-max-age 都没有在响应头中出现, 并且也没有其它缓存的设置, 那么浏览器默认会采用一个启发式的算法, 通常会取响应头的Date_value - Last-Modified_value值的10%作为缓存时间。(更多资料可参考:Caching in HTTP

Date:创建报文的日期时间, Last-Modified 服务器声明文档最后被修改时间

启发式缓存算法采用的缓存时间可长可短, 因此对于常规资源, 建议明确设置缓存时间(如指定max-age 或 expires).

查看网页中的请求使用的缓存策略

chrome进入开发者模式 -> Network窗口 -> size栏

chrome_size

状态码

状态码灰色表示强缓存,直接从缓存中获取资源。

304(Not Modified):HTTP 304(未改变)说明无需再次传输请求的内容,也就是说可以使用缓存的内容。(协商缓存)

302(Found):HTTP 302 重定向状态码表明请求的资源被暂时的移动到了由Location 头部指定的 URL 上。浏览器会重定向到这个URL, 但是搜索引擎不会对该资源的链接进行更新。

200:直接从缓存中获取资源,或向服务器发送请求,请求成功的状态。

缓存放置位置

memory cache:资源在内存中。内存缓存有两个特点 快速读取 和 时效性。快速读取,会将解析编译资源放入到进程中的内存,占据一定内存资源,方便下次快速读取 。时效性,一旦进程关闭(即浏览器关闭),进程的内存会被清空。

disk cache:资源在磁盘当中。如css文件。硬盘缓存将数据写入到磁盘文件,读取缓存需要读取硬盘文件进行 I/O 操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。

什么时候从内存缓存读取,什么时候从硬盘缓存读取?
在浏览器中,浏览器会在 JS 和图片等静态资源首次请求解析执行后直接存入内存缓存中,然后再存入到磁盘中。当刷新页面时,浏览器会直接从内存缓存 memory cache 中读取,如果内存中没有,则会从磁盘中读取缓存 disk cache,磁盘中读取完的数据会存到内存中,再次刷新页面时,会从内存中读取。

当关闭页面后,浏览器内存会清除当前页面的内存缓存memory cache,再次加载页面时,浏览器内存中此时没有加载过的数据缓存,则从磁盘中读取缓存。

操作系统老师要哭死了,我居然忘记了

控制css文件从缓存中读取

一般css文件都会进行缓存,最终数据会存在磁盘中。如果想要css文件更新,需要添加css版本控制,如hash命名,或者路径后添加?v1版本,但是后者部分代理缓存服务器不会缓存网址中包含 ? 的资源,所以会可能导致每次访问时重新请求文件。

css加载链接是跟HTML中的<link>标签中的href属性相关。

其他静态资源同理。

总结

首先查看是否具有缓存,会依次查看内存 -> 硬盘 是否有缓存数据,如果没有直接请求服务器,请求后的数据会缓存到内存和硬盘中。

如果有缓存,根据命中缓存策略进行使用缓存,强缓存不需要询问服务器,协商缓存需要询问服务器是否使用缓存。

协商缓存加载新数据后会更新旧缓存数据。

用户行为对浏览器缓存的控制

地址栏访问,链接跳转是正常用户行为,将会触发浏览器缓存机制;

F5刷新,浏览器会设置max-age=0,跳过强缓存判断,会进行协商缓存判断;

ctrl+F5刷新,跳过强缓存和协商缓存,直接从服务器拉取资源。

设置请求缓存机制

HTML header

参考链接:https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/meta#attr-http-equiv

利用HTML中的<meta>元素设置http-equiv属性。如果设置了 http-equiv 属性,meta 元素则是编译指令,提供的信息与类似命名的HTTP头部相同。

设置HTML文件缓存机制

<meta http-equiv="cache-control" content="no-cache"> <!-- 设置协商缓存 -->
<meta http-equiv="expires" content="0"> <!-- 已有cache-control则不生效 -->

<meta http-equiv="pragma" content="no-cache">已经不推荐使用了,参考链接:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Pragma,Prama已经废弃了

nginx配置

在server请求配置中,设置响应请求头内容。

服务端接口配置

服务端的接口自定义设置响应请求头。

ETag 生成

浏览器缓存 ETag 里的值是怎么生成的

参考链接

彻底理解浏览器的缓存机制 – 掘金

缓存(二)——浏览器缓存机制:强缓存、协商缓存

HTTP 缓存 – MDN

更多阅读

HTTP缓存控制小结