Aitter's Blog

浏览器的缓存机制

浏览器的缓存是一把双刃剑,用好了可以加快资源的加载速度,提升用户体验,如果使用不当,没有缓存,则加载缓慢,而且可能会给服务器造成巨大的压力,消耗带宽,缓存太严重,又无法及时更新内容导致体验急剧下降,了解浏览器的缓存机制有利用我们合理的使用缓存提升产品体验。

缓存主要是针对文件的重复请求情况下,浏览器可以根据协议头判断从服务器端请求文件还是从本地读取文件。

一般针对静态资源缓存
好处:

  1. 请求更快,缓存在本地或最近的CDN可以加快加载速度
  2. 节省宽带,已缓存的文件无需发送请求
  3. 降低服务器压力,大并发下访问,将静态资源放置在多个网络节点,起到负载均衡的作用

浏览器缓存中有一种HTML Meta标签中设置缓存

<META HTTP-EQUIV="Pragma" CONTENT="no-cache">

上述代码的作用是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。使用上很简单,但只有部分浏览器可以支持,而且所有缓存代理服务器都不支持,因为代理不解析HTML内容本身。极少使用。

强缓存与协商缓存

第一次请求

再次请求

强缓存

强缓存: 判断Expires/Cache-control, 如果命中,则不发请求,从本地获取缓存文件
协商缓存:发送请求至服务器,服务器从Request Header (Last-Modified/If-Modified-Since、Etag) 中判断是否返回文件,如果协商缓存命中,则返回304,不返回文件,告诉浏览器从本地缓存中取,如果未命中,则返回200和文件,浏览器收到文件更新本地缓存

Expires

描述的是一个绝对时间,由服务器返回,在Respones Headers中
是http1.0提出的一个表示资源过期时间的header
存储的是一个时间值,使用GMT格式表示

  1. 第一次请求时,服务器返回Expires在Header中,浏览器缓存这个header信息
  2. 当下次再次请求这个资源时,浏览器以请求的时间与这个Expires中的时间比对
    如果小于这个时间,说明未过期,直接从本地缓存中获取,请求返回200(from cache)
    Expires:Sun, 15 Jan 2017 05:56:46 GMT

获取缓存条件:缓存过期时间(服务器的)< 当前时间(客户端的)

缺点:Expires 是较老的强缓存管理header,由于它是服务器返回的一个绝对时间,这样存在一个问题,如果客户端的时间与服务器的时间相差很大(比如时钟不同步,或者跨时区),那么误差就很大,所以在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代

Cache-Control

描述的是一个相对时间
从http1.1开始应用

  1. 同样的,第一次请求时,服务器在response header中添加头Cache-Control的设置
    浏览器接收到这个资源后,连同这个header和本次请求的时间缓存在浏览器端
  2. 下一次再请求这个资源时,浏览器根据上一次请求的时间,这次请求的时间的时间差,去Cache-control 中设置的时间差比较,如果小于Cache-Control中设置的时间差,那么说明未过期,直接从本地缓存中取,请求返回200(from cache)
    Cache-Control:max-age=5184000

获取缓存条件:上次缓存时间(客户端)+ max-age < 当前时间 (客户端)

优先级
ExpiresCache-Control 同时存在时,Cache-Control 的优先级 大于 Expires 的优先级

Cache-Control 值可以是 public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age

各个消息中的指令含义如下:
Public 指示响应可被任何缓存区缓存。
Private 指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当前用户的部分响应消息,此响应消息对于其他用户的请求无效。
no-cache 指示请求或响应消息不能缓存,该选项并不是说可以设置”不缓存“,而是需要和服务器确认
no-store 在请求消息中发送将使得请求和响应消息都不使用缓存,完全不存下來。
max-age 指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。上次缓存时间(客户端的)+max-age(64200s)<客户端当前时间
min-fresh 指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
max-stale 指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。

适用性

一般用于静态资源使用,动态资源应该慎用,html页面也可以算是动态资源,如果html页面被缓存,html更新后就无法通知浏览器更新,所以一般html通常不加强缓存,以保证浏览器访问这些页面始终是最新的资源。
对于其它静态资源可以设置一个超长的Expire或Cache-Control,这样用户在第一次访问后,后面的访问就会从缓存中加载,速度会快很多。

协商缓存

Last-Modified / If-Modified-Since

标识资源的最后修改时间(由服务器返回)
配合Cache-Control使用

If-Modified-Since
重复请求时,当强缓存失效,发现请求头中有Last-Modified声明,则添加If-Modified-Since,内容为当前请求时间,发送请求,服务器获取请求时间与资源最后修改时间对比:
如果最后个性时间大于请求时间,则说明资源更新过,返回资源文件和状态200,
如果请求时间大于最后修改时间,则说明资源未修改,返回状态304告知浏览器使用缓存

过程

  1. 浏览器在第一次访问资源时,服务器返回资源的同时,在response header中添加 Last-Modified的header,值是这个资源在服务器上的最后修改时间,浏览器接收后缓存文件和header
  2. 浏览器下一次请求这个资源,浏览器检测到有 Last-Modified这个header,于是添加If-Modified-Since这个header,值是Last-Modified中的值
  3. 服务器再次收到这个资源请求,根据 If-Modified-Since 中的值与服务器中这个资源的最后修改时间对比,如果没有变化,返回304和空的响应体,如果If-Modified-Since的时间小于服务器中这个资源的最后修改时间,说明文件有更新,于是返回新的资源文件和200
Last-Modified:Tue, 31 May 2016 08:10:42 GMT
If-Modified-Since:Tue, 31 May 2016 08:10:42 GMT

缺点

  • Last-Modified 标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间(无法及时更新文件)
  • 如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存,有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形(无法使用缓存)。

HTTP1.1中Etag解决了上述问题

Etag / If-None-Match

配合Cache-Control使用

Etag: 由服务器返回,内容为资源的唯一标识,生成规则由服务器决定。 Apache中,Etag的值为文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。

If-None-Match
当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对:
如果相同,则返回304
如果不同,则返回200和资源文件

Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。

Etag 与 Last-Modified同时使用,Etag的优先级 大于 Last-Modified的优先级

过程
1.客户端请求一个页面(A)。
2.服务器返回页面A,并在给A加上一个ETag,值是这个资源的唯一标识,由服务器端生成。
3.客户端展现该页面,并将页面连同ETag一起缓存。
4.客户再次请求页面A,并将上次请求时服务器返回的ETag一起传递给服务器。
5.服务器检查该ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。

用户行为与缓存

  • 用户在地址栏回车、页面链接跳转、新开窗口、前进后退时,缓存是有效的
  • 用户在点击浏览器刷新或按 F5 时,Last-Modified/Etag 是有效的,但Expires、Cache-Control 重置失效
  • 用户在强制刷新按 Ctr+F5 时,缓存全部失效

常见状态码

请求缓存资源返回的状态码区别

  • 200:强缓Expires/Cache-Control存失效时,返回新的资源文件
  • 304(Not Modified ):协商缓存Last-modified/Etag没有过期时,服务端返回状态码304
  • 200(from cache): 强缓Expires/Cache-Control两者都存在,未过期,Cache-Control优先Expires时,浏览器从本地获取资源成功

应用

  1. 一般情况下,使用Cache-Control/Expires会配合Last-Modified/ETag一起使用,因为即使服务器设置缓存时间, 当用户点击“刷新”按钮时,浏览器会忽略缓存继续向服务器发送请求,这时Last-Modified/ETag将能够很好利用304,从而减少响应开销。

  2. 当用户在按F5进行刷新的时候,会忽略Expires/Cache-Control的设置,会再次发送请求去服务器请求,而Last-Modified/Etag还是有效的,服务器会根据情况判断返回304还是200

  3. 而当用户使用Ctrl+F5进行强制刷新的时候,会跳过强缓存和协商缓存,重新从服务器下载资源。

  4. 分布式系统里多台机器间文件的last-modified必须保持一致,以免负载均衡到不同机器导致比对失败
    分布式系统尽量关闭掉Etag(每台机器生成的etag都会不一样)

阅读参考

浏览器缓存知识小结及应用
浏览器 HTTP 协议缓存机制详解
浏览器缓存详解:expires,cache-control,last-modified,etag详细说明