和计算机存储体系一样,web中也存在着缓存,这些缓存可以让人们不必每次都访问地域上很遥远的web服务器,缓存的存在大大地减缓了网络拥塞。

HTTP 协议中就存在一些首部用于控制缓存,下面一一罗列并讲解:

缓存控制

Cache-Control:max-age=1000

max-age 定义了文档的最大使用期,从第一次生成文档到文档不再新鲜最大合法存在时间,它是相对于文档的创建时间来说的,单位为秒。

Expires:Fri,05 Jul 2002,05:00:00 GMT

指定一个具体的时间,这个时间之后文档就不再有效了。由于客户端设备上的系统时间可能有错误,所以有可能出现意外。

再验证

如果已经缓存的文档过期了,这个时候也不意味着它的内容已经发生了变化,这个时候缓存会向服务器发起再验证,缓存会获取一份这个文档新的副本,如果文档没有更新,那么缓存就将这个数据发回客户端,并更新相应的首部信息,包括新的过期时间。

但是如果从原服务器获取文档失败了,那么就不能发送已经过期的缓存了,而是发送错误报文。

用条件方法进行再验证

  • If-Modified-Since:

如果从指定日期后文档被修改了,就执行请求。如果从指定日期后文档没有被修改过,那就会返回一个304 Not Modified 响应,这个时候缓存一般之后发送一些变更过的头部信息。否则就会返回一个200 OK 响应。

  • If-None-Match:<tags>

有些时候仅仅使用最后修改时间来验证是不够的,因为有时候经管文档被修改了,但是修改并不重要到需要全球范围内缓存进行重装,或者尽管修改了,但是内容并没有变化(重写了文档),还有的服务器不能得到文档最后修改时间。为了解决这些问题 HTTP 允许对被称为 实体标签(ETag) 的版本标识符来比较。实体标签是附加到文档上的任意的标签。他们可能包含的是文档的版本号或者是序列号等。当发布者修改了文档后,可以修改这些实体标签来说明这是一个新的版本。这样缓存就可以使用 If-None-Match 条件首部来获取文档的新副本了。

// request
GET /about.html HTTP/1.0
If-None-Match:"v2.6","v2.5","v2.4"

// response
HTTP/1.0 304 Not Modified
ETag:"v2.6"

何时使用实体标签,何时该使用最后修改时间

如果服务器回送了一个实体标签,那么客户端就必须使用实体标签进行验证。如果服务器只回送了 Last-Modified 客户端就可以使用 If-Not-Midified 来验证。

如果服务器收到的请求既有If-Modified-Since 又有实体标签,那么只有两者都满足,才会回送 304 响应。

控制缓存的能力

Cache-Control:no-store|no-cache|must-revalidate|max-age

no-store

标识为 no-store 的响应,是不会进行缓存的,缓存就像非缓存代理一样向客户端转发该相应,然后删除该对象。

no-cache

标识为 no-cache 的响应,并非不会存储在缓存中,只是在与原服务器进行新鲜度验证之前,缓存是不能将其提供给客户端的。也就是说,每次访问该文档,都会进行新鲜度验证。

max-age

表示从服务器将文档传来时起,具有多少秒的新鲜时间。

must-revalidate

试探性过期

如果响应首部中没有 expires 和 Cache-Control:max-age 首部,那么缓存就会自己估计一个时间。可能会根据文档的最后修改时间来估计。最近修改的文档很有可能会再次修改,而很久以前修改过的文档很有可能是一份稳定的文档,因此缓存时间可能会较长。

客户端的新鲜度限制

对于用户点击 refresh 按钮这样的行为,是会无条件地从原始服务器中获取文档。当然在请求中头中也可以添加 Cache-Control 来限制文档的新鲜度。

Cache-Control:max-stale=<s>|min-fresh=<s>|no-cache|max-age|no-store|only-if-cached