HTTP 头中的 Cache-Control

发表于2017-01-15   1095次阅读

Cache-Control 是 HTTP 1.1 协议引入的参数,用于控制浏览器对页面的缓存。

Cache-Control 的常用参数包括:

控制缓存范围和是否可缓存的指令

public

表示响应可以被任何缓存区缓存(客户端和代理服务器都可缓存)。

如果响应被标记为public,即使有关联的 HTTP 认证,甚至响应状态码无法正常缓存,响应也可以被缓存。大多数情况下,public 不是必须的,因为明确的缓存信息(例如max-age)已表示 响应可以被缓存。

private

表示对于单个用户的响应,不能被共享缓存处理。私有的缓存区可以存储(仅客户端可以缓存,代理服务器不可缓存)。

相比之下,浏览器可以缓存 private 响应,但是通常只为单个用户缓存,因此,不允许任何中继缓存对其进行缓存 - 例如,用户浏览器可以缓存包含用户私人信息的 HTML 网页,但是 CDN 不能缓存。

no-cache

表示此请求或响应不能被缓存。在释放缓存的副本之前,强制将请求提交到源服务器进行验证(单不一定被下载)。

no-cache 表示必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。因此,如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,如果资源未被更改,可以避免下载。

no-store

相比 no-cache,no-store 更加简单,直接禁止浏览器和所有中继缓存存储返回的任何版本的响应 - 例如:一个包含个人隐私数据或银行数据的响应。每次用户请求该资源时,都会向服务器发送一个请求,每次都会下载完整的响应。

only-if-cached

表示不要去获取新数据。客户端只希望获取缓存的响应,并且不去联系服务器去检查是否存在较新的副本。

过期时间控制

max-age

以秒为单位,设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)。与 Expires 相反,时间是相对于请求的时间。

s-maxage

以秒为单位,覆盖 max-age 或者 Expires 头,但是仅适用于共享缓存(比如代理),并且可以被设置为私有缓存。

max-stale

以秒为单位,表面客户端愿意接收一个已经过期的资源。 可选的设置一个时间(单位秒),表示响应不能超过的过时时间。

min-fresh

以秒为单位,表示客户端希望在指定的时间内获取最新的响应。

s-maxage、max-stale 和 min-fresh 这三个指令并不常用。

重新验证和重新加载的控制指令

must-revalidate

缓存必须在使用之前验证旧资源的状态,并且不可使用过期资源。

其和 no-cache 指令有一些类似,主要区别可以参考这篇文章 [《web性能优化之:no-cache与must-revalidate深入探究》](https://segmentfault.com/a/1190000007317481

  • no-cache: 告诉浏览器、缓存服务器,不管本地副本是否过期,使用资源副本前,一定要到源服务器进行副本有效性校验。
  • must-revalidate:告诉浏览器、缓存服务器,本地副本过期前,可以使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验。

proxy-revalidate

与 must-revalidate 相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略。

no-transform

W3 官方解释如下:

Implementors of intermediate caches (proxies) have found it useful to convert the media type of certain entity bodies. A non- transparent proxy might, for example, convert between image formats in order to save cache space or to reduce the amount of traffic on a slow link.

Serious operational problems occur, however, when these transformations are applied to entity bodies intended for certain kinds of applications. For example, applications for medical imaging, scientific data analysis and those using end-to-end authentication, all depend on receiving an entity body that is bit for bit identical to the original entity-body. Therefore, if a message includes the no-transform directive, an intermediate cache or proxy MUST NOT change those headers that are listed in section 13.5.2 as being subject to the no-transform directive. This implies that the cache or proxy MUST NOT change any aspect of the entity-body that is specified by these headers, including the value of the entity-body itself.

MDN 解释如下

No transformations or conversions should made to the resource. The Content-Encoding, Content-Range, Content-Type headers must not be modified by a proxy. A non- transparent proxy might, for example, convert between image formats in order to save cache space or to reduce the amount of traffic on a slow link. The no-transform directive disallows this.

主要意思是改指令是给中间缓存看的(比如代理),中间缓存可能由于自身原因,比如存储空间有限,会改变图片大小,压缩返回等等。该指令告诉中间缓存不可修改。

该指令在 SEO 方面也很有用。比如百度有时会把 PC 站页面转成适合手机端浏览的页面,如果禁止百度转码,就可以使用该指令,通过 HTTP 响应头和页面的 Meta 标签都可以。

例子

通过 HTML 的 META 进行设置

例如:

<meta http-equiv="Cache-Control" content="max-age=7200" />

通过 HTTP 响应头进行设置

禁止缓存

Cache-Control: no-cache, no-store, must-revalidate

缓存静态资源

Cache-Control:public, max-age=31536000

ETag

确保服务器提供验证令牌 (ETag):通过验证令牌,如果服务器上的资源未被更改,就不必传输相同的字节。

让我们假设在首次获取资源 120 秒之后,浏览器又对该资源发起了新请求。首先,浏览器会检查本地缓存并找到之前的响应,不幸的是,这个响应现在已经'过期',无法在使用。此时,浏览器也可以直接发出新请求,获取新的完整响应,但是这样做效率较低,因为如果资源未被更改过,我们就没有理由再去下载与缓存中已有的完全相同的字节。

这就是 ETag 头中指定的验证令牌所要解决的问题:服务器会生成并返回一个随机令牌,通常是文件内容的哈希值或者某个其他指纹码。客户端不必了解指纹码是如何生成的,只需要在下一个请求中将其发送给服务器:如果指纹码仍然一致,说明资源未被修改,我们就可以跳过下载。

关于 Etag 的更详细介绍可以参考这篇文章 《HTTP 缓存》

扩展阅读