HTTP 缓存技术

HTTP 缓存有两种实现方式,分别是强制缓存协商缓存

强制缓存

强制缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边。

强制缓存是利用下面这两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期:

  • Cache-Control,是一个相对时间;

  • Expires,是一个绝对时间;

如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,Cache-Control 的优先级高于 Expires。

具体的实现流程如下:

  • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 Cache-Control,Cache-Control 中设置了过期时间;

  • 浏览器再次请求访问服务器中的该资源时,会先比较 Cache-Control 中设置的过期时间,判断该资源是否过期,如果没有,则使用该缓存,否则重新请求服务器;

  • 服务器再次收到请求后,会再次发送 Response 头部 Cache-Control。

协商缓存

304 响应码告诉浏览器可以使用本地缓存的资源,通常这种通过服务端告知客户端是否可以使用缓存的方式被称为协商缓存

协商缓存可以基于两种头部来实现。

  • 第一种:请求头部中的 If-Modified-Since 字段与响应头部中的 Last-Modified 字段实现,这两个字段的意思是:

    • 响应头部中的 Last-Modified:标示这个响应资源的最后修改时间;

    • 请求头部中的 If-Modified-Since:最近一次收到的服务端返回的响应中的 Last-Modified;

    服务器收到请求后发现有 If-Modified-Since 则与被请求资源的最后修改时间进行对比(Last-Modified)

    • 如果最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK;

    • 如果最后修改时间较旧(小),说明资源无新修改,响应 HTTP 304 走缓存。

  • 第二种:请求头部中的 If-None-Match 字段与响应头部中的 ETag 字段,这两个字段的意思是:

    • 响应头部中 Etag:唯一标识响应资源;

    • 请求头部中的 If-None-Match:最近一次收到的服务端返回的响应中的 Etag;

    服务器收到请求后对 If-None-Match 与请求资源的 Etag 进行比对,如果资源没有变化返回 304,如果资源变化了返回 200。

第一种实现方式是基于时间实现的,第二种实现方式是基于一个唯一标识实现的,相对来说后者可以更加准确地判断文件内容是否被修改,避免由于时间篡改导致的不可靠问题。

ETag 的优先级比 Last-Modified 的优先级高,这是因为 ETag 能解决 Last-Modified 几个比较难以解决的问题:

  • 在没有修改文件内容情况下文件的最后修改时间可能也会改变,这会导致客户端认为这文件被改动了,从而重新请求;

  • 可能有些文件是在秒级以内修改的,If-Modified-Since 能检查到的粒度是秒级的,使用 Etag就能够保证这种需求下客户端在 1 秒内能刷新多次;

  • 有些服务器不能精确获取文件的最后修改时间。

注意

协商缓存这两个字段都需要配合强制缓存中 Cache-Control 字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求

当使用 ETag 字段实现的协商缓存的过程:

  • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 ETag 唯一标识,这个唯一标识的值是根据当前请求的资源生成的;

  • 当浏览器再次请求访问服务器中的该资源时,首先会先检查强制缓存是否过期

    • 如果没有过期,则直接使用本地缓存;

    • 如果缓存过期了,会在 Request 头部加上 If-None-Match 字段,该字段的值就是 ETag 唯一标识;

  • 服务器再次收到请求后,会根据请求中的 If-None-Match 值与当前请求的资源生成的唯一标识进行比较

    • 如果值相等,则返回 304 Not Modified,不会返回资源

    • 如果不相等,则返回 200 状态码和返回资源,并在 Response 头部加上新的 ETag 唯一标识;

  • 如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。

Last updated