Cache-Control 标头

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

HTTP Cache-Control 标头在请求和响应中都包含指令(说明),用于控制浏览器和共享缓存(例如代理、CDN)中的缓存

头类型 请求标头, 响应标头
禁止请求头
CORS-safelisted 响应头

语法

http
Cache-Control: <directive>, <directive>, ...

缓存指令遵循以下规则

  • 缓存指令不区分大小写。但是,建议使用小写,因为某些实现不识别大写指令。
  • 允许多个指令,并且必须用逗号分隔(例如,Cache-control: max-age=180, public)。
  • 有些指令带有一个可选参数。当提供参数时,它与指令名称用等号(=)分隔。通常,指令的参数是整数,因此不使用引号括起来(例如,Cache-control: max-age=12)。

缓存指令

下表列出了标准的 Cache-Control 指令

Request Response
max-age max-age
max-stale -
min-fresh -
- s-maxage
no-cache no-cache
no-store no-store
no-transform no-transform
only-if-cached -
- must-revalidate
- proxy-revalidate
- must-understand
- private
- public
- immutable
- stale-while-revalidate
stale-if-error stale-if-error

注意:请查看兼容性表以了解其支持情况;不识别它们的 user agent 应该忽略它们。

术语

本节定义了本文档中使用的术语,其中一些来自规范。

(HTTP) 缓存

保存请求和响应以供后续请求重用的实现。它可以是共享缓存,也可以是私有缓存。

共享缓存

存在于源服务器和客户端之间的缓存(例如代理、CDN)。它存储单个响应并将其重用于多个用户——因此开发人员应避免将个性化内容存储在共享缓存中。

私有缓存

存在于客户端中的缓存。它也被称为本地缓存浏览器缓存。它可以为单个用户存储和重用个性化内容。

存储响应

当响应可缓存时,将响应存储在缓存中。但是,缓存的响应并不总是按原样重用。(通常,“缓存”意味着存储响应。)

重用响应

重用缓存的响应以供后续请求。

重新验证响应

询问源服务器存储的响应是否仍然新鲜。通常,重新验证通过条件请求完成。

新鲜响应

表示响应是新鲜的。这通常意味着可以根据请求指令将响应重用于后续请求。

陈旧响应

表示响应是陈旧响应。这通常意味着响应不能按原样重用。缓存存储不需要立即删除陈旧响应,因为重新验证可以将响应从陈旧状态更改为再次新鲜状态。

Age

自响应生成以来的时间。它是判断响应是否新鲜或陈旧的标准。

指令

本节列出了影响缓存的指令——包括响应指令和请求指令。

响应指令

max-age

max-age=N 响应指令表示响应在生成后 N 秒内保持新鲜

http
Cache-Control: max-age=604800

表示缓存在响应新鲜时可以存储此响应并将其用于后续请求。

请注意,max-age 不是自收到响应以来的经过时间;它是自源服务器生成响应以来的经过时间。因此,如果其他缓存(在响应所走的网络路径上)存储响应 100 秒(使用 Age 响应标头字段指示),则浏览器缓存将从其新鲜度生命周期中扣除 100 秒。

如果 max-age 值为负数(例如 -1)或不是整数(例如 3599.99),则缓存行为未指定。鼓励缓存将该值视为 0(这在 HTTP 规范的计算新鲜度生命周期部分中提到)。

http
Cache-Control: max-age=604800
Age: 100

s-maxage

s-maxage 响应指令表示响应在共享缓存中保持新鲜的时间。s-maxage 指令被私有缓存忽略,如果存在,则会覆盖 max-age 指令或 Expires 标头为共享缓存指定的值。

http
Cache-Control: s-maxage=604800

no-cache

no-cache 响应指令表示响应可以存储在缓存中,但在每次重用之前,即使缓存与源服务器断开连接,也必须与源服务器进行验证。

http
Cache-Control: no-cache

如果您希望缓存在重用存储内容时始终检查内容更新,则应使用 no-cache 指令。它通过要求缓存重新验证每个请求与源服务器来实现此目的。

请注意,no-cache 并不意味着“不要缓存”。no-cache 允许缓存存储响应,但要求它们在重用之前重新验证。如果您想要的“不要缓存”的含义实际上是“不要存储”,那么应使用 no-store 指令。

must-revalidate

must-revalidate 响应指令表示响应可以存储在缓存中,并且在新鲜时可以重用。如果响应变得陈旧,则在重用之前必须与源服务器进行验证。

通常,must-revalidatemax-age 一起使用。

http
Cache-Control: max-age=604800, must-revalidate

HTTP 允许缓存在与源服务器断开连接时重用陈旧响应must-revalidate 是一种防止这种情况发生的方法——存储的响应要么与源服务器重新验证,要么生成 504(网关超时)响应。

proxy-revalidate

proxy-revalidate 响应指令等同于 must-revalidate,但专门用于共享缓存。

no-store

no-store 响应指令表示任何类型的缓存(私有或共享)都不应存储此响应。

http
Cache-Control: no-store

private

private 响应指令表示响应只能存储在私有缓存中(例如,浏览器中的本地缓存)。

http
Cache-Control: private

您应该为用户个性化内容添加 private 指令,特别是对于登录后收到的响应和通过 Cookie 管理的会话。

如果您忘记为包含个性化内容的响应添加 private,则该响应可以存储在共享缓存中,并最终被多个用户重用,这可能导致个人信息泄露。

public

public 响应指令表示响应可以存储在共享缓存中。带有 Authorization 标头字段的请求的响应不得存储在共享缓存中;但是,public 指令将导致此类响应存储在共享缓存中。

http
Cache-Control: public

通常,当页面处于基本身份验证或摘要身份验证下时,浏览器会发送带有 Authorization 标头的请求。这意味着响应是针对受限用户(拥有帐户的用户)进行访问控制的,并且即使它具有 max-age,它也基本上不能被共享缓存。

您可以使用 public 指令解除该限制。

http
Cache-Control: public, max-age=604800

请注意,s-maxagemust-revalidate 也会解除该限制。

如果请求没有 Authorization 标头,或者您已经在响应中使用了 s-maxagemust-revalidate,则无需使用 public

must-understand

must-understand 响应指令表示缓存应仅在它根据状态码理解缓存要求的情况下存储响应。

must-understand 应与 no-store 结合使用以实现回退行为。

http
Cache-Control: must-understand, no-store

如果缓存不支持 must-understand,它将被忽略。如果 no-store 也存在,则不存储响应。

如果缓存支持 must-understand,它将存储响应,同时理解基于其状态码的缓存要求。

no-transform

一些中间件出于各种原因转换内容。例如,一些中间件转换图像以减小传输大小。在某些情况下,这对内容提供商来说是不希望的。

no-transform 表示任何中间件(无论它是否实现缓存)都不应转换响应内容。

immutable

immutable 响应指令表示响应在新鲜时不会更新。

http
Cache-Control: public, max-age=604800, immutable

静态资源的一种现代最佳实践是在其 URL 中包含版本/哈希,同时从不修改资源——而是,在必要时,使用具有新版本号/哈希的更新版本来更新资源,以便它们的 URL 不同。这被称为缓存失效模式。

html
<script src="https://example.com/react.0.0.0.js"></script>

当用户重新加载浏览器时,浏览器将发送条件请求以向源服务器进行验证。但是,即使用户重新加载浏览器,也没有必要重新验证这些静态资源,因为它们从未被修改。immutable 告诉缓存响应在新鲜时是不可变的,并避免了对服务器进行此类不必要的条件请求。

当您对资源使用缓存失效模式并将其应用于长 max-age 时,您还可以添加 immutable 以避免重新验证。

stale-while-revalidate

stale-while-revalidate 响应指令表示缓存可以在重新验证时重用陈旧响应。

http
Cache-Control: max-age=604800, stale-while-revalidate=86400

在上面的示例中,响应新鲜 7 天(604800 秒)。7 天后它会变得陈旧,但缓存被允许在接下来的 1 天(86400 秒)内重用它,前提是它们在后台重新验证响应。

重新验证将使缓存再次新鲜,因此对客户端来说,它在此期间始终是新鲜的——有效地向它们隐藏了重新验证的延迟惩罚。

如果在此期间没有发生请求,缓存将变为陈旧,并且下一个请求将正常重新验证。

stale-if-error

stale-if-error 响应指令表示当上游服务器生成错误或本地生成错误时,缓存可以重用陈旧响应。在此处,错误被视为状态码为 500、502、503 或 504 的任何响应。

http
Cache-Control: max-age=604800, stale-if-error=86400

在上面的示例中,响应新鲜 7 天(604800 秒)。之后,它会变得陈旧,但在遇到错误时可以额外使用 1 天(86400 秒)。

在 stale-if-error 期限过后,客户端将收到生成的任何错误。

请求指令

no-cache

no-cache 请求指令要求缓存在重用之前与源服务器验证响应。

http
Cache-Control: no-cache

no-cache 允许客户端即使缓存有新鲜响应,也可以请求最新的响应。

当用户强制重新加载页面时,浏览器通常会在请求中添加 no-cache

no-store

no-store 请求指令允许客户端请求缓存不要存储请求和相应的响应——即使源服务器的响应可以存储。

http
Cache-Control: no-store

max-age

max-age=N 请求指令表示客户端允许源服务器在 N 秒内生成的存储响应——其中 N 可以是任何非负整数(包括 0)。

http
Cache-Control: max-age=10800

在上述情况下,如果带有 Cache-Control: max-age=10800 的响应是在 3 小时前生成的(根据 max-ageAge 标头计算),则缓存无法重用该响应。

许多浏览器使用此指令进行重新加载,如下所述。

http
Cache-Control: max-age=0

max-age=0no-cache 的一种变通方法,因为许多旧(HTTP/1.0)缓存实现不支持 no-cache。最近的浏览器在“重新加载”中仍然使用 max-age=0——为了向后兼容——并且交替使用 no-cache 来导致“强制重新加载”。

如果 max-age 值为负数(例如 -1)或不是整数(例如 3599.99),则缓存行为未指定。鼓励缓存将该值视为 0

max-stale

max-stale=N 请求指令表示客户端允许在 N 秒内陈旧的存储响应。如果未指定 N 值,则客户端将接受任何年龄的陈旧响应。

http
Cache-Control: max-stale=3600

例如,带有上述标头的请求表示浏览器将接受缓存中在过去一小时内过期的陈旧响应。

当源服务器宕机或太慢并且可以接受缓存中即使有点旧的响应时,客户端可以使用此标头。

请注意,主流浏览器不支持带有 max-stale 的请求。

min-fresh

min-fresh=N 请求指令表示客户端允许至少在 N 秒内新鲜的存储响应。

http
Cache-Control: min-fresh=600

在上述情况下,如果带有 Cache-Control: max-age=3600 的响应在 51 分钟前存储在缓存中,则缓存无法重用该响应。

当用户要求响应不仅新鲜,而且要求它在一段时间内不会更新时,客户端可以使用此标头。

请注意,主流浏览器不支持带有 min-fresh 的请求。

no-transform

no-transform 对响应的含义相同,但用于请求。

only-if-cached

客户端指示应返回已缓存的响应。如果缓存有存储的响应,即使是陈旧的,也将返回。如果没有可用的缓存响应,将返回 504 网关超时响应。

stale-if-error

stale-if-error 请求指令表示浏览器有兴趣在任何中间服务器出错时接收特定源的陈旧内容。这不受任何浏览器支持(请参阅浏览器兼容性)。

使用案例

防止存储

如果您不希望响应存储在缓存中,请使用 no-store 指令。

http
Cache-Control: no-store

请注意,no-cache 意味着“可以存储,但在验证之前不要重用”——因此它不是为了防止响应被存储。

http
Cache-Control: no-cache

理论上,如果指令冲突,应遵守最严格的指令。因此,下面的示例基本上没有意义,因为 privateno-cachemax-age=0must-revalidateno-store 冲突。

http
# conflicted
Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate

# equivalent to
Cache-Control: no-store

使用“缓存失效”缓存静态资产

当您使用版本控制/哈希机制构建静态资产时,将版本/哈希添加到文件名或查询字符串是管理缓存的好方法。

例如

html
<!-- index.html -->
<script src="/assets/react.min.js"></script>
<img src="/assets/hero.png" width="900" height="400" />

当您更新库时,React 库版本将更改,当您编辑图片时,hero.png 也会更改。因此,它们很难用 max-age 存储在缓存中。

在这种情况下,您可以通过使用特定编号的库版本并在其 URL 中包含图片的哈希来满足缓存需求。

html
<!-- index.html -->
<script src="/assets/react.0.0.0min.js"></script>
<img src="/assets/hero.png?hash=deadbeef" width="900" height="400" />

您可以添加一个长的 max-age 值和 immutable,因为内容永远不会更改。

http
# /assets/*
Cache-Control: max-age=31536000, immutable

当您更新库或编辑图片时,新内容应具有新的 URL,并且缓存不会重用。这被称为“缓存失效”模式。

使用 no-cache 确保 HTML 响应本身未被缓存。no-cache 可能会导致重新验证,并且客户端将正确接收新版本的 HTML 响应和静态资产。

http
# /index.html
Cache-Control: no-cache

注意:如果 index.html 受基本身份验证或摘要身份验证控制,则 /assets 下的文件不会存储在共享缓存中。如果 /assets/ 文件适合存储在共享缓存中,您还需要 publics-maxagemust-revalidate 之一。

始终保持最新内容

对于动态生成或静态但经常更新的内容,您希望用户始终收到最新版本。

如果您不添加 Cache-Control 标头,因为响应不打算缓存,这可能会导致意外结果。缓存存储被允许启发式地缓存它——因此,如果您对缓存有任何要求,您应该始终在 Cache-Control 标头中明确指出它们。

向响应添加 no-cache 会导致对服务器进行重新验证,因此您每次都可以提供新鲜响应——或者如果客户端已经有一个新响应,则只需响应 304 Not Modified

http
Cache-Control: no-cache

大多数 HTTP/1.0 缓存不支持 no-cache 指令,因此历史上 max-age=0 被用作一种变通方法。但是,只有 max-age=0 可能会导致缓存在与源服务器断开连接时重用陈旧响应must-revalidate 解决了这个问题。这就是为什么下面的示例等同于 no-cache

http
Cache-Control: max-age=0, must-revalidate

但是现在,您可以简单地使用 no-cache 来代替。

清除已存储的缓存

没有缓存指令可以清除中间服务器上已存储的响应。

想象一下,客户端/缓存存储了一个路径的新鲜响应,并且没有向服务器发出请求。服务器无法对该路径执行任何操作。

Clear-Site-Data: cache 可用于清除浏览器缓存中站点的所有存储响应,因此请谨慎使用。请注意,这不会影响共享或中间缓存。

规范

规范
HTTP 缓存
# field.cache-control
HTTP 不可变响应
# the-immutable-cache-control-extension

浏览器兼容性

另见