672fb4ce-28f9-498d-9140-b3ff9f47d62f

  1. 浏览器在加载资源时,根据请求头的expirescache-control判断是否命中强缓存,是则直接从缓存读取资源,不会发请求到服务器。

  2. 如果没有命中强缓存,浏览器一定会发送一个请求到服务器,通过last-modifiedetag验证资源是否命中协商缓存,如果命中,服务器会将这个请求返回,但是不会返回这个资源的数据,依然是从缓存中读取资源

  3. 如果前面两者都没有命中,直接从服务器加载资源

相同点

如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;

#### 不同点

  • 强缓存不发请求到服务器

  • 协商缓存会发请求到服务器。

强制缓存

  • Cache-Control: no-store没有缓存。缓存中不得存储任何关于客户端请求和服务端响应的内容。每次由客户端发起的请求都会下载完整的响应内容。

  • Cache-Control: no-cache缓存但每次都验证。带着相关验证字段将请求发送到服务器。服务器会验证所描述的缓存是否过期。若未过期(304 NOT MODIFIED),则使用本地缓存。

    私有和共有

  • Cache-Control: public:可以被所有用户缓存(多用户共享),包括终端和CDN等中间代理服务器 Cache-Control: private:只能被终端浏览器缓存(而且是私有缓存),不允许中继缓存服务器进行缓存。

    过期

  • Cache-Control: max-age=<seconds>:表示资源能够被缓存(保持新鲜)的最大时间。

    验证方式

  • Cache-Control: must-revalidate:意味着缓存在考虑使用一个陈旧的资源时,必须先验证它的状态,已过期的缓存将不被使用。

Pragma

PragmaHTTP/1.0标准中定义的一个header属性,请求中包含Pragma的效果跟在头信息中定义Cache-Control: no-cache相同,但是HTTP的响应头没有明确定义这个属性,所以它不能拿来完全替代HTTP/1.1中定义的Cache-control头。通常定义Pragma以向后兼容基于HTTP/1.0的客户端。

协商缓存

用户点击刷新按钮时会开始缓存验证。如果缓存的响应头信息里含有"Cache-control: must-revalidate”的定义,在浏览的过程中也会触发缓存验证。另外,在浏览器偏好设置里设置Advanced->Cache为强制验证缓存也能达到相同的效果。

当缓存的文档过期后,需要进行缓存验证或者重新获取资源。只有在服务器返回强校验器或者弱校验器时才会进行验证。

强校验器 ETag If-None-Match

响应头:Etag

请求头:If-None-Match

If-None-Match: <etag_value>
If-None-Match: <etag_value>, <etag_value>, …
If-None-Match: *

If-None-Match 是一个条件式请求首部。对于 GETGETHEAD 请求方法来说,当且仅当服务器上没有任何资源的 ETag 属性值与这个首部中列出的相匹配的时候,服务器端会才返回所请求的资源,响应码为 200 。对于其他方法来说,当且仅当最终确认没有已存在的资源的 ETag 属性值与这个首部中所列出的相匹配的时候,才会对请求进行相应的处理。

对于 GETHEAD 方法来说,当验证失败的时候,服务器端必须返回响应码 304 (Not Modified,未改变)。对于能够引发服务器状态改变的方法,则返回 412 (Precondition Failed,前置条件失败)。

服务器端在生成状态码为 304 的响应的时候,必须同时生成以下会存在于对应的 200 响应中的首部:Cache-Control、Content-Location、Date、ETag、Expires 和 Vary 。

弱校验器

响应头:Last-Modified

请求头:If-Modified-Since

说他弱是因为只能精确到 1s。如果响应头里含有这个信息,客户端可以在后续的请求中If-Modified-Since来验证缓存。

两者Etag优先级更高。

选择Etag通常是因为:

  • 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
  • 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
  • 某些服务器不能精确的得到文件的最后修改时间。

如何选择合适的缓存

大致的顺序

  • Cache-Control —— 请求服务器之前
  • Expires —— 请求服务器之前
  • If-None-Match (Etag) —— 请求服务器
  • If-Modified-Since (Last-Modified) —— 请求服务器

协商缓存需要配合强缓存使用,如果不启用强缓存的话,协商缓存根本没有意义

大部分web服务器都默认开启协商缓存,而且是同时启用【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】

但是下面的场景需要注意:

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

https://github.com/amandakelake/blog/issues/41