浏览器第一次向一个 web 服务器发起 http 请求后,服务器会返回请求的资源,并且在响应头中添加一些有关缓存的字段如:Cache-ControlExpiresLast-ModifiedETagDate等等。之后浏览器再向该服务器请求该资源就可以视情况使用强缓存协商缓存

  1. 强缓存:浏览器直接从本地缓存中获取数据,不与服务器进行交互。
  2. 协商缓存:浏览器发送请求到服务器,服务器判定是否可使用本地缓存。

联系与区别:两种缓存方式最终使用的都是本地缓存;前者无需与服务器交互,后者需要。

1. 强缓存

用户发起了一个 http 请求后,浏览器发现先本地已有所请求资源的缓存,便开始检查缓存是否过期。有两个 http 头部字段控制缓存的有效期:ExpiresCache-Control,浏览器是根据以下两步来判定缓存是否过期的

  • **Expires: ** response header 里的过期时间,浏览器再次加载资源时,如果在这个过期时间内,则命中强缓存。
  • Cache-Control: 当值设为 max-age=300 时,则代表在这个请求正确返回时间的 5 分钟内再次加载资源,就会命中强缓存。

区别:Expires 是 http1.0 的产物,Cache-Control 是 http1.1 的产物两者同时存在的话,Cache-Control优先级高于 Expires。Expires 是过时的产物,现阶段它的存在只是一种兼容性的写法

2. 协商缓存

当浏览器发现缓存过期后,缓存并不一定不能使用了,因为服务器端的资源可能仍然没有改变,所以需要与服务器协商,让服务器判断本地缓存是否还能使用。此时浏览器会判断缓存中是否有 ETagLast-Modified 字段,如果没有,则发起一个 http 请求,服务器根据请求返回资源;

如果有这两个字段,则在请求头中添加 If-None-Match 字段(有ETag字段的话添加)、If-Modified-Since字段(有Last-Modified字段的话添加)。

注意:如果同时发送If-None-MatchIf-Modified-Since字段,服务器只要比较If-None-MatchETag的内容是否一致即可;如果内容一致,服务器认为缓存仍然可用,则返回状态码304,浏览器直接读取本地缓存,这就完成了协商缓存的过程,也就是图中的蓝线;如果内容不一致,则视情况返回其他状态码,并返回所请求资源。下面详细解释下这个过程:

ETagIf-None-Match

二者的值都是服务器为每份资源分配的唯一标识字符串。

  1. 浏览器请求资源,服务器会在响应报文头中加入 ETag 字段。资源更新时,服务器端的 ETag 值也随之更新
  2. 浏览器再次请求资源时,会在请求报文头中添加 If-None-Match 字段,它的值就是上次响应报文中的 ETag的值
  3. 服务器会比对 ETagIf-None-Match 的值是否一致,如果不一致,服务器则接受请求,返回更新后的资源;如果一致,表明资源未更新,则返回状态码为 304 的响应,可继续使用本地缓存,要注意的是,此时响应头会加上 ETag 字段,即使它没有变化。

Last-ModifiedIf-Modified-Since

二者的值都是GMT格式的时间字符串。

  1. 浏览器第一次向服务器请求资源后,服务器会在响应头中加上 Last-Modified 字段,表明该资源最后一次的修改时间
  2. 浏览器再次请求该资源时,会在请求报文头中添加 If-Modified-Since 字段,它的值就是上次服务器响应报文中的 Last-Modified 的值
  3. 服务器会比对 Last-ModifiedIf-Modified-Since 的值是否一致,如果不一致,服务器则接受请求,返回更新后的资源;如果一致,表明资源未更新,则返回状态码为 304 的响应,可继续使用本地缓存,与 ETag 不同的是:此时响应头中不会再添加 Last-Modified 字段。

ETag 较之 Last-Modified 的优势

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

这时,利用 ETag 能够更加准确的控制缓存,因为 ETag 是服务器自动生成的资源在服务器端的唯一标识符,资源每次变动,都会生成新的 ETag 值。Last-Modified ETag是可以一起使用的,但服务器会优先验证ETag

Web 缓存器又称 代理服务器(proxy server),它能够将最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,如果代理服务器中没有这个请求,那么就会从互联网中访问该资源,将其返回给代理服务器,代理服务器会保存一份资源的副本,之后将资源返回给客户端。若代理服务器发现这个请求与暂时存放的请求相同,就返回暂存响应,而不需按URL的地址再次去互联网访问该资源。

  1. 大大减少对客户请求的响应的时间
  2. 可以避免多次从源服务器转发资源,大大减少了一个机构接入链路到因特网的通信量,从而降低了费用

3. 缓存相关 Header

4. 浏览器缓存过程

  1. 浏览器第一次加载资源,服务器返回200,浏览器将资源文件从服务器上请求下载下来,并把 response header 及该请求的返回时间(要与 Cache-Control 和 Expires 对比)一并缓存;
  2. 下一次加载资源时,先比较当前时间和上一次返回 200 时的时间差,如果没有超过 Cache-Control 设置的 max-age,则没有过期,命中强缓存,不发请求直接从本地缓存读取该文件(如果浏览器不支持 HTTP1.1,则用 Expires 判断是否过期);
  3. 如果时间过期,则向服务器发送 header 带有 If-None-MatchIf-Modified-Since 的请求;
  4. 服务器收到请求后,优先根据 Etag 的值判断被请求的文件有没有做修改,Etag 值一致则没有修改,命中协商缓存,返回304;如果不一致则有改动,直接返回新的资源文件带上新的Etag值并返回 200;
  5. 如果服务器收到的请求没有 Etag 值,则将 If-Modified-Since 和被请求文件的最后修改时间做比对,一致则命中协商缓存,返回304;不一致则返回新的last-modified和文件并返回 200;

5. 如何不缓存

5.1. Cache-Control

no-store: 不使用任何缓存

no-cache: 仍然对资源使用缓存,但每一次在使用缓存之前必须向服务器对缓存资源进行验证。

5.2. Expires

设为当前时间之前

5.3. 前端开发设置不缓存

1
2
3
<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">