HTTP的缓存机制

远子 •  2021年03月22日

在浏览器中,用户点击刷新按钮时会开始缓存验证,通过设置 Cache-Control: must-revalidate 也会触发缓存验证。另外,在浏览器偏好设置里设置 Advanced -> Cache 为强制验证缓存也能达到相同的效果。

浏览器的缓存有两种:强制缓存和协商缓存。

整体流程:

img

强制缓存

资源被强制缓存时,不需要经过服务器,直接读取浏览器缓存。

在 Chrome 的 Network 中显示的 HTTP 状态码是 200,在 Chrome 中,强缓存又分为 Disk Cache(存在硬盘里)和 Memory Cache(存在内存中),存放的位置是由浏览器控制的。

资源是否被强制缓存由 ExpiresCache-ControlPragma 3 个 Header 属性共同控制。

Exipres

Expires 的值是一个 HTTP 日期,在浏览器发起请求时,会根据系统时间和 Expires 的值进行比较,如果系统时间超过了 Expires 的值,则缓存失效,发起请求重新获取新资源。

当客户端时间和系统时间不一致时,会出现缓存有效期不准确的问题。

在 3 个头部属性中 Expires 的优先级最低。

Cache-Control

Cache-Control 是 HTTP/1.1 中新的 属性,常用的值有:

  • Cache-Control: no-store:禁止使用缓存,每次都请求最新资源;
  • Cache-Control: no-cache:不使用强缓存,需要与服务器验证缓存是否有效;
  • Cache-Control: max-age=<秒>:设置缓存在指定秒数内有效;
  • Cache-Control: private:专用于个人的缓存,代理、CDN等不能缓存此资源;
  • Cache-Control: public:代理、CDN可以缓存此资源;
  • Cache-Control: must-revalidate:在缓存过期前可以使用,过期后必须向服务器验证。

Pragma

Pragma 只有一个值,Pragma: no-cache,效果和 Cache-Control: no-cache 一致。

在 3 个头部属性中 Pragma 优先级最高。

协商缓存

当强缓存无效(失效或者禁用)并且在请求头中设置了 If-Modified-Since 或者 If-None-Match 的时候,服务器会根据这两个值验证是否命中协商缓存,如果命中返回 304,并且响应头会设置 Last-ModifiedETag 属性。

协商缓存有两种校验器:

  • 弱校验器(Last-Modify/If-Modify-Since),客户端第一次请求一个资源时,响应头中会携带 Last-Modify,其值是该资源最后修改时间;客户端再次请求该资源时,请求头中会携带 If-Modify-Since,其值为缓存之前返回的 Last-Modify,服务器收到 If-Modify-Since 后,和 Last-Modify 比对,判定是否命中缓存。
  • 强校验器(ETag),客户端请求一个资源时,服务器在响应头中加入该资源的唯一标识 ETag(规则由服务器决定),客户端再次请求该资源时,请求头中会携带 If-None-Match,其值为 ETag,服务器收到后和资源最新的 ETag 比对,判定资源是否更改。

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

ETag 和 Last-Modified 的区别:

  1. ETag 更加精准。Last-Modified 的精度是秒,如果一秒内多次改动了资源,Last-Modified 不会变化,但 ETag 每次都会变化;
  2. ETag 性能上逊于 Last-Modified,ETag 需要计算 hash 值,Last-Modified 只需要记录时间;
  3. 优先使用 ETag

ETag 还有一个用途:防止资源被同时更改。

(完)