通过网络提取资源既缓慢又昂贵:
- 大型响应需要在浏览器和服务器之间进行多次往返。
- 只有在网页的所有关键资源都已完全下载后,网页才会加载。
- 如果用户使用的是流量有限的移动流量套餐,那么每一次不必要的网络请求都会浪费他们的钱。
如何避免发出不必要的网络请求?浏览器的 HTTP 缓存是第一道防线。这不一定是最强大或最灵活的方法,而且您对缓存响应的生命周期的控制有限,但它很有效,所有浏览器都支持它,而且不需要太多工作。
本指南介绍了有效 HTTP 缓存实现的基础知识。
浏览器兼容性
实际上,没有一个名为 HTTP 缓存的单独 API。这是一系列 Web 平台 API 的通用名称。所有浏览器都支持以下 API:
Cache-Control
ETag
Last-Modified
HTTP 缓存的运作方式
浏览器发出的所有 HTTP 请求都会先路由到浏览器缓存,以检查是否有可用于执行请求的有效缓存响应。如果有匹配项,系统会从缓存中读取响应,从而消除网络延迟和传输产生的数据费用。
HTTP 缓存的行为由请求标头和响应标头共同控制。在理想情况下,您可以同时控制 Web 应用的代码(用于确定请求标头)和 Web 服务器的配置(用于确定响应标头)。
如需更深入地了解相关概念,请参阅 MDN 的 HTTP 缓存一文。
请求标头:请使用默认值(通常)
您的 Web 应用的传出请求中应包含许多重要标头,但浏览器在发出请求时几乎总会代表您设置这些标头。影响新鲜度检查的请求标头(例如 If-None-Match
和 If-Modified-Since
)会根据浏览器对 HTTP 缓存中当前值的理解而显示。
这是一个好消息,这意味着您可以继续在 HTML 中添加 <img src="my-image.png">
等标记,浏览器会自动为您处理 HTTP 缓存,而无需您额外付出努力。
响应标头:配置 Web 服务器
HTTP 缓存设置中最关键的部分是 Web 服务器向每个传出响应添加的标头。以下标头都会影响有效的缓存行为:
Cache-Control
。服务器可以返回Cache-Control
指令,以指定浏览器和其他中间缓存应如何以及多长时间缓存各个响应。ETag
。当浏览器发现已过期的缓存响应时,可以向服务器发送一个小型令牌(通常是文件内容的哈希值),以检查文件是否已更改。如果服务器返回相同的令牌,则表示文件相同,无需重新下载。Last-Modified
。此标头的用途与ETag
相同,但使用基于时间的策略来确定资源是否发生了更改,而ETag
使用的是基于内容的策略。
某些 Web 服务器默认内置了对设置这些标头的支持,而其他 Web 服务器则会完全忽略这些标头,除非您明确进行配置。如何配置标头的具体详情因您使用的 Web 服务器而异,因此您应参阅服务器的文档,获取最准确的详细信息。
为方便您查找,下面列出了配置一些热门 Web 服务器的说明:
省略 Cache-Control
响应标头并不会停用 HTTP 缓存!相反,浏览器会有效地猜测哪种类型的缓存行为最适合给定类型的内容。您可能希望获得更精细的控制,因此请花些时间配置响应标头。
您应使用哪些响应标头值?
在配置 Web 服务器的响应标头时,您应涵盖以下两种重要场景。
针对有版本控制的网址的长效缓存
假设您的服务器指示浏览器将 CSS 文件缓存 1 年 (Cache-Control: max-age=31536000
),但您的设计师刚刚进行了一项紧急更新,您需要立即部署。如何通知浏览器更新文件的“过时”缓存副本?您无法更改,至少需要更改资源的网址。
浏览器缓存响应后,系统会使用缓存的版本,直到该版本不再新鲜(由 max-age
或 expires
决定),或者因某些其他原因(例如用户清除浏览器缓存)而被从缓存中驱逐。因此,在构建网页时,不同用户最终可能会使用文件的不同版本:刚提取资源的用户使用新版本,而缓存了较早(但仍有效)副本的用户使用较低版本的响应。
如何同时兼顾客户端缓存和快速更新这两种方式的优势?您更改资源的网址,并在其内容发生变化时强制用户下载新响应。通常,您可以通过在文件名中嵌入文件的指纹或版本号(例如 style.x234dff.css
)来实现此目的。
在响应包含“指纹”或版本信息且内容永远不会更改的网址的请求时,请在响应中添加 Cache-Control: max-age=31536000
。
设置此值会告知浏览器,如果它在接下来一年内(31,536,000 秒;支持的最大值)的任何时间需要加载同一网址,则可以立即使用 HTTP 缓存中的值,而无需向您的 Web 服务器发出网络请求。太棒了,您已立即获得了避免使用网络带来的可靠性和速度!
webpack 等构建工具可以自动执行向资源网址分配哈希指纹的流程。
对无版本网址进行服务器重新验证
很抱歉,您加载的部分网址没有版本号。您可能无法在部署 Web 应用之前添加构建步骤,因此无法向资源网址添加哈希。而且,每个 Web 应用都需要 HTML 文件,这些文件(几乎)永远不会包含版本信息,因为如果用户需要记住要访问的网址是 https://example.com/index.34def12.html
,就不会费心使用您的 Web 应用。那么,您可以对这些网址执行哪些操作?
这是您需要承认失败的一个场景。仅靠 HTTP 缓存还不足以完全避免使用网络。(别担心,您很快就会了解服务工件,它将为您提供所需的支持,帮助您扭转局势。)不过,您可以采取一些措施,确保网络请求尽可能快速高效。
以下 Cache-Control
值可帮助您微调未版本化网址的缓存位置和方式:
no-cache
。这会指示浏览器每次都必须先与服务器重新验证,然后才能使用网址的缓存版本。no-store
。这会指示浏览器和其他中间缓存(例如 CDN)永远不要存储文件的任何版本。private
。浏览器可以缓存该文件,但中间缓存无法缓存。public
。响应可以由任何缓存存储。
请参阅附录:Cache-Control
流程图,直观了解确定要使用的 Cache-Control
值的过程。Cache-Control
还可以接受以英文逗号分隔的指令列表。请参阅附录:Cache-Control
示例。
设置 ETag
或 Last-Modified
也可以有所帮助。如响应标头中所述,ETag
和 Last-Modified
具有相同的用途:确定浏览器是否需要重新下载已过期的缓存文件。我们建议使用 ETag
,因为它更准确。
假设从初始提取到现在已经过去了 120 秒,并且浏览器已针对同一资源发起了新请求。首先,浏览器会检查 HTTP 缓存并找到之前的响应。很遗憾,由于响应现已过期,因此浏览器无法使用之前的响应。此时,浏览器可以调度新请求并提取新的完整响应。不过,这样做效率不高,因为如果资源没有更改,就没有理由下载已在缓存中的信息!
验证令牌(如 ETag
标头中所指定)旨在解决此问题。服务器会生成并返回一个任意令牌,该令牌通常是文件内容的哈希值或其他指纹。浏览器无需知道如何生成指纹;只需在下次请求时将其发送到服务器即可。如果指纹保持不变,则表示资源未更改,浏览器可以跳过下载。
设置 ETag
或 Last-Modified
可让重新验证请求触发请求标头中提及的 If-Modified-Since
或 If-None-Match
请求标头,从而大大提高重新验证请求的效率。
当正确配置的 Web 服务器看到这些传入请求标头时,它可以确认浏览器在其 HTTP 缓存中已有的资源版本是否与 Web 服务器上的最新版本一致。如果匹配,服务器可以使用 304 Not Modified
HTTP 响应进行响应,这相当于说“嘿,继续使用您已有的内容!”发送此类响应时,传输的数据非常少,因此通常比实际发回请求的实际资源的副本要快得多。

/file
,并包含 If-None-Match
标头,以指示服务器仅在服务器上文件的 ETag
与浏览器的 If-None-Match
值不匹配时返回完整文件。在本例中,这两个值确实匹配,因此服务器会返回 304 Not Modified
响应,其中包含有关应将文件缓存多长时间的说明 (Cache-Control: max-age=120
)。
摘要
HTTP 缓存是一种有效的提升加载性能的方式,因为它可以减少不必要的网络请求。所有浏览器都支持此功能,而且设置起来也不费事。
以下 Cache-Control
配置是一个不错的起点:
Cache-Control: no-cache
- 适用于每次使用前都应与服务器重新验证的资源。Cache-Control: no-store
(对于绝不应缓存的资源)。Cache-Control: max-age=31536000
,适用于版本化资源。
ETag
或 Last-Modified
标头有助于您更高效地重新验证已过期的缓存资源。
了解详情
如果您想了解 Cache-Control
标头的使用基础知识以外的内容,请参阅 Jake Archibald 的缓存最佳实践和最大有效期注意事项指南。
如需有关如何针对回访者优化缓存用量的指导,请参阅善用缓存。
附录:更多提示
如果您有更多时间,可以通过以下方式进一步优化 HTTP 缓存的使用:
- 使用一致的网址。如果您在不同的网址上提供相同的内容,系统会多次提取和存储该内容。
- 最大限度地减少用户流失。如果资源的一部分(例如 CSS 文件)会频繁更新,而文件的其余部分不会更新(例如库代码),请考虑将频繁更新的代码拆分到单独的文件中,并针对频繁更新的代码使用短时效缓存策略,针对不经常更改的代码使用长时效缓存策略。
- 如果您的
Cache-Control
政策允许一定程度的过时,请查看新的stale-while-revalidate
指令。
附录:Cache-Control
流程图

Cache-Control
标头的决策流程。
附录:Cache-Control
示例
Cache-Control 值 |
说明 |
---|---|
max-age=86400 |
浏览器和中间缓存最多可将响应缓存 1 天(60 秒 x 60 分钟 x 24 小时)。 |
private, max-age=600 |
浏览器(但不是中间缓存)最多可以将响应缓存 10 分钟(60 秒 x 10 分钟)。 |
public, max-age=31536000 |
任何缓存都可以将响应存储 1 年。 |
no-store |
响应不得缓存,且必须在每次请求时完整提取。 |