【http】http缓存

2021/09/09 14:31:19

作用

浏览器根据缓存策略将 http 请求的内容缓存下来,发送请求后先读取缓存,没有缓存再发送请求,可以减轻服务器压力、提高用户体验。

缓存相关的 http 响应头常见字段

  • expires:设置缓存过期的时间
  • private:客户端可以缓存
  • public:客户端和代理服务器都可缓存
  • cache-control: max-age=xxx:缓存的内容将在 xxx 秒后失效
  • no-cache:需要使用对比缓存来验证缓存数据
  • no-store:所有内容都不会缓存,强制缓存,对比缓存都不会触发
  • last-modified:内容上次被修改的时间
  • Etag:文件的特殊标识
规则消息包头值/示例类型作用
过期机制Pragmano-cache响应告诉浏览器忽略资源的缓存副本,每次访问都需要去服务器拉取【http1.0 中存在的字段,在 http1.1 已被抛弃,使用 Cache-Control 替代,但为了做 http 协议的向下兼容,很多网站依旧会带上这个字段】
ExpiresMon, 15 Aug 2016 03:56:47 GMT响应启用缓存和定义缓存时间。告诉浏览器资源缓存过期时间,如果还没过该时间点则不发请求【http1.0 中存在的字段,该字段所定义的缓存时间是相对服务器上的时间而言的,如果客户端上的时间跟服务器上的时间不一致(特别是用户修改了自己电脑的系统时间),那缓存时间可能就没啥意义了。在 HTTP 1.1 版开始,使用 Cache-Control: max-age=秒替代】
Cache-Controlno-cache响应告诉浏览器忽略资源的缓存副本,强制每次请求直接发送给服务器,拉取资源,但不是“不缓存”
no-store响应强制缓存在任何情况下都不要保留任何副本``
max-age=[秒]响应指明缓存副本的有效时长,从请求时间开始到过期时间之间的秒数
public响应任何路径的缓存者(本地缓存、代理服务器),可以无条件的缓存改资源
private响应只针对单个用户或者实体(不同用户、窗口)缓存资源
Last-ModifiedMon, 15 Aug 2016 03:56:47 GMT响应告诉浏览器这个资源最后的修改时间。服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端【只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件`的修改时间】```
If-Modified-SinceMon, 15 Aug 2016 03:56:47 GMT请求其值为上次响应头的 Last-Modified 值,再次向 web 服务器请求时带上头 If-Modified-Since。web 服务器收到请求后发现有头 If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),包括更新 Last-Modified 的值,HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应 HTTP 304(无需包体,节省浏览),告知浏览器继续使用所保存的 cache
验证机制ETag"fd56273325a2114818df4f29a628226d"响应告诉浏览器当前资源在服务器的唯一标识符(生成规则又服务器决定)
If-None-Match"fd56273325a2114818df4f29a628226d"请求当资源过期时(使用 Cache-Control 标识的 max-age),发现资源具有 Etage 声明,则再次向 web 服务器请求时带上头 If-None-Match(Etag 的值)。web 服务器收到请求后发现有头 If-None-Match 则与被请求资源的相应校验串进行比对,决定返回 200 或 304

部分字段的关系和区别

  • Cache-Control 和 Expires

Cache-Control 与 Expires 的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过 Cache-Control 的选择更多,设置更细致,如果同时设置的话,其优先级高于 Expires。

  • Last-Modified/ETag 与 Cache-Control/Expires

配置 Last-Modified/ETag 的情况下,浏览器再次访问统一 URI 的资源,还是会发送请求到服务器询问文件是否已经修改,如果没有修改,服务器会只发送一个 304 给浏览器,告诉浏览器直接从自己本地的缓存取数据;如果修改过那就整个数据重新发给浏览器;

Cache-Control/Expires 则不同,如果检测到本地的缓存还是有效的时间范围内,浏览器直接使用本地副本,不会发送任何请求。两者一起使用时,Cache-Control/Expires 的优先级要高于 Last-Modified/ETag。即当本地副本根据 Cache-Control/Expires 发现还在有效期内时,则不会再次发送请求去服务器询问修改时间(Last-Modified)或实体标识(Etag)了。

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

  • Last-Modified 与 ETag

Last-Modified 与 ETag 是可以一起使用的,服务器会优先验证 ETag,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304。

ETag 的出现是为了解决 Last-Modified 的几个问题:

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

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

关于 ETag 需要注意

Etag 是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存,但是需要注意的是分布式系统里多台机器间文件的 last-modified 必须保持一致,以免负载均衡到不同机器导致比对失败,Yahoo 建议分布式系统尽量关闭掉 Etag(每台机器生成的 etag 都会不一样,因为除了 last-modified、inode 也很难保持一致)。

Last-Modified/If-Modified-Since 要配合 Cache-Control 使用,Etag/If-None-Match 也要配合 Cache-Control 使用。

缓存类型

缓存类型可以分为强制缓存与协商缓存。强制缓存表示有缓存时必须使用缓存;协商缓存根据一系列条件来判断是否可以使用缓存。

强制缓存优先级高于协商缓存

强制缓存

强制缓存存在一个问题,该缓存方式优先级高,如果在过期时间内缓存的资源在服务器上更新了,客服端不能及时获取最新的资源。

expires

expires 设置的时间是基于服务器时间的。

expires 给浏览器设置了一个绝对时间,当浏览器时间超过这个绝对时间之后,重新向服务器发送请求。

Expires: Fri, 04 Jan 2019 12:00:00 GMT

这个方法简单直接,直接设定一个绝对的时间 (当前时间+缓存时间)。但是也存在隐患,例如浏览器当前时间是可以进行更改的,更改之后 expires 设置的绝对时间相对不准确,cache 可能会出现长久不过期或者很快就过期的情况。

cache-control: max-age

为了解决 expires 存在的问题,Http1.1 版本中提出了 cache-control: max-age,该字段与 expires 的缓存思路相同,都是设置了一个过期时间,不同的是 max-age 设置的是相对缓存时间开始往后多久,因此不存在受日期不准确情况的影响。

协商缓存

协商缓存解决了无法及时获取更新资源的问题。以下两组字段,都可以对资源做标识,由服务器做分析,如果未进行更新,那返回 304 状态码,从缓存中读取资源,否则重新请求资源。

last-modify

last-modify 告知了客户端上次修改该资源的时间

浏览器将这个值记录在 if-modify-since 中(浏览器自动记录了该字段信息),下一次请求相同资源时,与服务器返回的 last-modify 进行比对,如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。

last-modify 以秒为单位进行更新,如果小于该单位高频进行更新的话,不适合采用该方法。

ETag

请求该资源成功之后,将返回的 ETag 存入 if-none-match 字段中(浏览器自动记录了该字段信息),同样在请求资源时传递给服务器,服务器查询该编码对应的资源有无更新,无更新返回 304 状态,更新返回 200 并重新请求。

ETag 是针对某个文件的特殊标识,服务器默认采用 SHA256 算法生成。也可以采用其他方式,保证编码的唯一性即可。

缓存优先级

Cache-Control > Expires > ETag > Last-Modified

如果资源需要用到强制缓存,Cache-Control 相对更加安全,协商缓存中利用 ETag 查询更新更加全面。

缓存存储位置

disk cache

存储在硬盘中的缓存,不会随着浏览器的关闭而消失。

当硬盘中的资源被加载时,浏览器会将资源存储在内存(memory cache)中,下次读取直接从 memory cache 中读取。

memory cache

存储在内存中的缓存,会随着 tab、浏览器的关闭而释放。

当接口状态返回 304 时,资源默认存储在 memory cache 中,当页面关闭后,重新打开需要再次请求。

缓存读取顺序

  • 先去内存看,如果有,直接加载
  • 如果内存没有,择取硬盘获取,如果有直接加载
  • 如果硬盘也没有,那么就进行网络请求
  • 加载到的资源缓存到硬盘和内存,下次请求可以快速从内存中获取到

http 状态码 200 和 304

200 from memory cache

不访问服务器,直接读缓存,从内存中读取缓存。此时的数据时缓存到内存中的,当关闭进程后,也就是浏览器关闭以后,数据将不存在。

但是这种方式只能缓存派生资源。

200 from disk cache

不访问服务器,直接读缓存,从磁盘中读取缓存,当关闭进程时,数据还是存在。

这种方式也只能缓存派生资源

304 Not Modified

访问服务器,发现数据没有更新,服务器返回此状态码。然后从缓存中读取数据。

用户行为与缓存

用户操作Expires/Cache-ControlLast-Modified/Etag
地址栏回车有效有效
页面链接跳转有效有效
新开窗口有效有效
前进、后退有效有效
F5 刷新无效(BR 重置 max-age=0)有效
Ctrl+F5 刷新无效(重置 Cache-Control=no-cache)无效(请求头丢弃该选项

无法缓存的请求

  1. HTTP 信息头中包含 Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或 Cache-Control:max-age=0 等告诉浏览器不用缓存的请求

  2. 需要根据 Cookie,认证信息等决定输入内容的动态请求是不能被缓存的

  3. 经过 HTTPS 安全加密的请求(有人也经过测试发现,ie 其实在头部加入 Cache-Control:max-age 信息,firefox 在头部加入 Cache-Control:Public 之后,能够对 HTTPS 的资源进行缓存,参考《HTTPS 的七个误解open in new window》)

  4. POST 请求无法被缓存

  5. HTTP 响应头中不包含 Last-Modified/Etag,也不包含 Cache-Control/Expires 的请求无法被缓存

参考

从前端角度理解缓存open in new window浏览器缓存机制详解open in new window