HTTP缓存策略:强缓存与协商缓存

Posted by Mars . Modified at

HTTP缓存: 强缓存与协商缓存

一、HTTP缓存

HTTP缓存针对HTTP响应报文,一般只对GET和HEAD方法响应报文有效。(POST响应在罕见特殊配置下也可以缓存,具体见MDN

HTTP缓存可以存在于浏览器本地,也可以存在于代理服务器。

1. 缓存相关的首部行

1.1 强缓存

优先级从高到低:

Pragma -> Cache-Control -> Expires

Pragma和Expires都是HTTP1.0的首部。

Pragma设置为no-cache(唯一有效值),则意味着每次请求都无法执行强缓存,只能进行协商缓存

Expires设置的是一个GMT格式的绝对日期,意味着不超过这个时间节点,都会触发强缓存。(浏览器会把它和客户端系统时间比对,如果服务器和本地有时间差,缓存判断容易产生误差。)

Expires: Thu, 01 Dec 1994 16:00:00 GMT (必须是GMT格式)

Cache-Control可以设置如下参数,一次可以设置多个,中间用逗号隔开:

  • 客户端请求中的Cache-Control:
    • no-cache: 与Pragma一样,每次请求都无法执行强缓存,只能进行协商缓存,但是比Pragma优先级要低;
    • no-store: 不进行缓存。强缓存和协商缓存都不会触发;
    • max-age: 设置缓存相对过期时长(单位是秒),相对于请求的时间;
    • max-stale: 表示客户端愿意接受一个过期的响应,最大过期时长不能超过max-stale设置的值(秒);
    • min-fresh: 表示客户端希望获取一个能在指定的秒数内保持其最新状态的响应。
  • 服务器响应中的Cache-Control:
    • public:表示响应可以被任何对象缓存,包括客户端和中间代理服务器等;
    • private: 表示响应只能被单个用户缓存,不能被中间代理服务器缓存;
    • no-cache: 与Pragma一样,每次请求都无法执行强缓存,只能进行协商缓存,但是比Pragma优先级要低;
    • no-store: 不进行缓存。强缓存和协商缓存都不会触发;
    • max-age: 设置缓存相对过期时长(单位是秒),相对于请求的时间;
    • must-revalidate:一旦资源过期(比如已经超过max-age),在成功向原始服务器验证之前,缓存不能用该资源响应后续请求;
    • immutable: 资源不会发生变化。告诉浏览器不要发送协商缓存验证请求头(比如If-modified-since),即使用户手动刷新页面。

1.2 协商缓存

  • 客户端请求:
    • If-Modified-Since: 如果响应头中有Last-Modified,则协商缓存请求头中携带这个字段,值为Last-Modified的值,表示询问服务器资源在这个时间节点之前是否修改过;
    • If-None-Match: 如果响应头中有ETag,则协商缓存请求头中携带这个字段,表示询问服务器资源的ETag是否有变化;
  • 服务端响应:
    • Last-Modified: 资源上次更新的时间;
    • ETag: 资源通过摘要算法计算的Tag值,资源一旦有任何修改,这个值会变化。

2. 强缓存与协商缓存触发策略

2.1. 强缓存

浏览器执行请求时,如果发现本地有之前请求的缓存,先查看请求缓存中的首部行,判断是否命中强缓存:

  1. 如果Cache-Control(优先)存在且设置了max-age值,则计算此次请求的age值ageValue,并与max-age进行比较。如果ageValue>max-age则强缓存触发失败,反之则触发强缓存。

HTTP中缓存的使用期计算(Age Calculation)

HTTP1.1协议要求,当一个响应报文是从缓存里获取的时候,HTTP/1.1协议要求在响应报文中必须添加一个Age首部行。它的值表示的是,从这个响应报文在源服务器中产生或者过期验证的那一刻起,到现在为止所经过时间的一个估计值(从名字上其实就看的出来,它表示的是缓存的年龄)。经常和max-age一起来验证缓存是否过期,即如果这个字段的值比max-age的值还大,那这份缓存就已经过期了。

这个ageValue值的计算,是缓存到达本地时带有的age值initAge,加上这次请求时间点为止,缓存在本地经过的时长agePassed。

age = initAge + agePassed

  1. 如果Cache-Control不存在,但设置了Expires,判断请求的Date是否超过Expires设置的时间,未过期则直接命中强缓存。

Expires: 是GMT格式字符串(绝对值),意味着何时过期,它来自于服务器时间。浏览器在请求进行比较的时候,使用的是系统时间,系统时间可以修改所以相对不可靠。

这时,直接从缓存中读取响应(包含响应头),不与服务器通信。(状态码200)

2.2 协商缓存

如果发现 Cache-Control 或 Expires 二者之一有过期,则发送请求到服务器:

如果缓存首部存在Etag,则发送带If-None-Match的请求;(优先级更高)

如果缓存首部存在Last-Modified,则发送带If-Modified-Since的请求。

ETag 与 Last-Modified 的区别?

ETag是服务器根据资源内容,自动生成的唯一的ID。它更能体现资源是否已修改。

Last-Modified主要是有以下三点问题:

① 最短修改时间只能精确到秒;

② 有些文件在服务器周期性保存,内容并未修改,这时造成本地缓存的浪费;

③ 某些服务器系统不能得到精确的修改时间。

由服务器根据这两个字段判断,缓存是否还可以使用。

如果可以,则意味着协商缓存命中,服务器返回新的响应header信息,但是不带有响应主体。(意味着服务器仍可从缓存中读取响应)(校验码304)

如果校验失败,服务器返回带响应主体的响应报文。(校验码200)

2.3 用户行为对缓存的影响

F5会忽略强缓存,保留协商缓存。

Ctrl+F5会忽视全部缓存。

2.4 如何保证每次资源更新浏览器都会及时更新,防止从缓存读取?

  • 设置Cache-Controlno-store
  • 为每一个更新的资源,配置一个独有的资源名。常用的是在资源后面加上query ID后缀。
Keywords: Network HTTP
previousPost nextPost
已经有 1000000 个小伙伴看完了这篇推文。