HTTP缓存的三种方式详解

5,540 阅读5分钟

浏览器缓存

所谓浏览器缓存其实就是指在本地使用的计算机中开辟一个内存区,同时也开辟一个硬盘区作为数据传输的缓冲区,然后用这个缓冲区来暂时保存用户以前访问过的信息。

HTTP 缓存主要是通过请求和响应报文头中的对应 Header 信息,来控制缓存的策略。

HTTP缓存可以缩短网页请求资源的距离,减少延迟,节省网络流量,并且由于缓存文件可以重复利用,降低网络负荷,提高客户端响应。

根据是否需要重新向服务器发起请求,可分为强缓存和协商缓存

强缓存

定义:当命中强缓存的时候,客户端不会再请求服务器,直接从缓存中读取内容,并返回HTTP状态码200。

强制缓存,在响应头由 Expires、Cache-Control 和 Pragma控制

  • Expires:值为服务器返回的过期时间,浏览器再次加载资源时,如果在这个过期时间内,则命中强缓存。(HTTP1.0的属性,缺点是客户端和服务器时间不一致会导致命中误差)
  • Cache-Control:HTTP1.1属性,优先级更高,以下为常用属性
    • no-store: 禁用缓存
    • no-cache:不使用强缓存,每次需向服务器验证缓存是否失效
    • private/public:private指的单个用户,public可以被任何中间人、CDN等缓存
    • max-age=:max-age是距离请求发起的时间的秒数
    • must-revalidate:在缓存过期前可以使用,过期后必须向服务器验证
  • Pragma
    • no-cache:效果和cache-control等no-cache一致。 优先级Pragma > Cache-Control > Expires
强缓存的资源存储位置
状态Network - Size含义
200from memory cache不请求网络资源,资源在内存,
一般是脚本、字体、图片,浏览器关闭,数据将被释放
200from disk cache请求网络资源,资源在磁盘,
一般是css等,关闭数据还在
200资源大小从服务器下载最新资源
304报文大小请求服务端发现资源未更新,使用本地资源

协商缓存

定义:向服务器发送请求,服务器会根据这个请求的请求头的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的响应头通知浏览器从缓存中读取资源

协商缓存,响应头中有两个字段标记规则

  • Last-Modified / If-Modified-Since
    • Last-Modified是浏览器第一个请求资源,服务器响应头字段,是资源文件最后一次更改时间(精确到秒)。
    • 下一次发送请求时,请求头里的If-Modified-Since就是之前的Last-Modified
    • 服务器更加最后修改时间判断命中,如果命中,http为304且不返回资源、不返回last-modify
  • Etag / If-None-Match:Etag 的校验优先级高于 Last-Modified
    • Etag是加载资源时,服务器返回的响应头字段,是对资源的唯一标记,值是hash码。
    • 浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的Etag值放到请求头里的If-None-Match
    • 服务器接受到If-None-Match的值后,会拿来跟该资源文件的Etag值做比较,如果相同,则表示资源文件没有发生改变,命中协商缓存。

在精确度上,Etag要优于Last-Modified,Last-Modified的时间单位是秒,如果某个文件在1秒内改变了多次,那么他们的Last-Modified其实并没有体现出来修改,但是Etag每次都会改变确保了精度 在性能上,Etag要逊于Last-Modified,毕竟Last-Modified只需要记录时间,而Etag需要服务器通过算法来计算出一个hash值。 在优先级上,服务器校验优先考虑Etag。

用户行为对强缓存和协商缓存的影响

用户操作Expires/cache-controlLast-modified/Etag
地址栏回车
页面链接跳转
新开窗口
前进、后退
有效有效
F5刷新无效有效
Ctrl + F5刷新(强制刷新)无效无效

启发式缓存

MDN解释: 对于含有特定头信息的请求,会去计算缓存寿命。比如Cache-control: max-age=N的头,相应的缓存的寿命就是N。通常情况下,对于不含这个属性的请求则会去查看是否包含Expires属性,通过比较Expires的值和头里面Date属性的值来判断是否缓存还有效。如果max-age和expires属性都没有,找找头里的Last-Modified信息。如果有,缓存的寿命就等于头里面Date的值减去Last-Modified的值除以10(注:根据rfc2626其实也就是乘以10%)

简而言之,只有在没有明确缓存策略时,会激活启发式缓存。所以要合理设置缓存,否则会因没有设置缓存时间等原因,导致内容缓存不刷新。

// Date 减去 Last-Modified 值的 10% 作为缓存时间。
// Date:创建报文的日期时间, Last-Modified 服务器声明文档最后被修改时间
  response_is_fresh = (Date -  Last-Modified) % 10

附:

一、设置不缓存的方法
  1. html文件设置meta;
<meta http-equiv="pragma" content="no-cache"> 
<meta http-equiv="Cache-Control" content="no-cache, must-revalidate"> 
<meta http-equiv="expires" content="Wed, 26 Feb 1997 00:00:00 GMT">
  1. 服务端响应添加Cache-Control:no-cache,must-revalidate指令;
  2. 修改请求头If-modified-since:0If-none-match
  3. 请求url后增加时间戳;
  4. 服务端设置Cache-Control:private指令,防止代理服务器缓存资源
二、 为什么同一个资源有时是from memory cache有时是from disk cache?

Chrome会根据本地内存的使用率来决定缓存存放在哪,如果内存使用率很高,放在磁盘里面,内存的使用率很高会暂时放在内存里面

三、Cache-Control: max-age=0 和 no-cache有什么不同?

max-age=0no-cache应该是从语气上不同。max-age=0是告诉客户端资源的缓存到期应该向服务器验证缓存的有效性。而no-cache则告诉客户端使用缓存前必须向服务器验证缓存的有效性。

参考文档