借助“早期提示”利用服务器思考时间来加快页面加载速度

了解您的服务器如何向浏览器发送有关关键子资源的提示。

Kenji Baheux
Kenji Baheux

什么是“早期提示”?

随着时间的推移,网站变得更加复杂。因此,服务器需要执行一些重要的工作(例如,访问数据库,或访问源服务器的 CDN)来为请求的网页生成 HTML,这种情况很常见。遗憾的是,这种“服务器思考时间”会导致浏览器在开始呈现网页之前出现额外的延迟。事实上,只要服务器需要准备响应,该连接就会实际进入空闲状态。

显示加载网页和加载其他资源之间的服务器思考时间差为 200 毫秒的图片。
如果没有早期提示:在确定如何响应主要资源时,服务器上的所有内容都会被阻止。

Early Hints 是一个 HTTP 状态代码 (103 Early Hints),用于在最终响应之前发送初步 HTTP 响应。这样,当服务器忙于生成主要资源时,服务器就可以向浏览器发送关于可能使用的关键子资源(例如,网页的样式表、关键 JavaScript)或来源的提示。浏览器可以使用这些提示预热连接,并请求子资源,同时等待主资源。换句话说,Early Hints 通过提前执行一些工作来帮助浏览器充分利用这种“服务器思考时间”,从而加快页面加载速度。

显示 Early Hints 如何允许网页发送部分响应的图片。
使用早期提示:服务器在确定最终响应时,可以提供带有资源提示的部分响应

在某些情况下,Largest Contentful Paint 的性能提升可能从几百毫秒(由 ShopifyCloudflare 观察到)提升,最多可提升 1 秒(如前后对比所示):

两个网站的对比。
使用 WebPageTest (Moto G4 - DSL) 比较测试网站上的早期提示之前/之后

如何使用“早期提示”

要使用“早期提示”功能,第一步是确定热门着陆页,即用户访问您的网站时通常从哪些页面开始。如果您的许多用户都来自其他网站,则可能是首页或热门商品详情页。这些入口点比其他页面更重要的原因在于,当用户浏览您的网站时,Early Hints 的实用性会降低(即,浏览器更有可能在后续的第二次或第三次导航中拥有所需的全部子资源)。给人留下良好的第一印象也始终是一个好主意!

现在,您已获得这个按优先级排序的着陆页列表,下一步就是确定哪些源或子资源适合用于 preconnectpreload 提示。通常,这些资源是对关键用户指标贡献最大的来源和子资源,例如 Largest Contentful PaintFirst Contentful Paint。具体而言,查找阻塞渲染的子资源,如同步 JavaScript、样式表,甚至是网络字体。同样,请寻找托管对关键用户指标有很大影响的子资源的源站。

另请注意,如果您的主资源已在使用 preconnectpreload,您可以考虑将这些源或候选资源作为 Early Hints 的候选资源。如需了解详情,请参阅如何优化 LCP。但是,单纯地将 preconnectpreload 指令从 HTML 复制到 Early Hints 可能不是最佳选择

在 HTML 中使用这些参数时,您通常需要对 Preload Scanner 无法在 HTML 中发现的资源(例如,本来会晚到的字体或背景图片)执行 preconnectpreload 操作。对于 Early Hints,您没有 HTML,因此可能需要 preconnect 到关键域或 preload 原本应该在 HTML 中提前发现的关键资源(例如,预加载 main.cssapp.js)。此外,并非所有浏览器都支持将 Early Hints 设置为 preload。请参阅浏览器支持

第二步包括尽可能降低对可能已过时或不再被主资源使用的资源或源使用早期提示的风险。例如,频繁更新和版本控制的资源(例如 example.com/css/main.fa231e9c.css)可能不是最佳选择。请注意,此问题并不特定于早期提示,而是适用于可能存在的任何 preloadpreconnect。这是最适合自动化或模板化处理的细节(例如,手动流程更有可能导致 preload 与使用资源的实际 HTML 标记之间的哈希或版本网址不匹配)。

例如,请考虑以下流程:

GET /main.html
Host: example.com
User-Agent: [....] Chrome/103.0.0.0 [...]

服务器预测将需要 main.abcd100.css,并建议使用 Early Hints 预加载它:

103 Early Hints
Link: </main.abcd100.css>; rel=preload; as=style
[...]

片刻之后,该网页(包括关联的 CSS)即会投放。遗憾的是,此 CSS 资源会频繁更新,并且主要资源已经比预测的 CSS 资源 (abcd100) 提前了五个版本 (abcd105)。

200 OK
[...]
<HTML>
<head>
   <title>Example</title>
   <link rel="stylesheet" href="/main.abcd105.css">

一般来说,应力求相当稳定且在很大程度上独立于主要资源结果的资源和源站。如有必要,您可以考虑将关键资源一分为二:与 Early Hints 配合使用的稳定部分,以及浏览器收到主要资源后等待提取的更具动态性的部分:

<HTML>
<head>
   <title>Example</title>
   <link rel="stylesheet" href="/main.css">
   <link rel="stylesheet" href="/experimental.3eab3290.css">

最后,在服务器端,查找已知支持早期提示的浏览器发送的主要资源请求,并立即使用 103 个早期提示进行响应。在 103 响应中,包含相关的预连接和预加载提示。主资源准备就绪后,应发送常规响应(例如,如果成功,则发送 200 OK)。为了实现向后兼容性,最好在最终响应中包含 Link HTTP 标头,甚至可以添加在生成主要资源的过程中显而易见的关键资源(例如,如果您遵循“一分为二”建议,则包含关键资源的动态部分)。如下所示:

GET /main.html
Host: example.com
User-Agent: [....] Chrome/103.0.0.0 [...]
103 Early Hints
Link: <https://fonts.google.com>; rel=preconnect
Link: </main.css>; rel=preload; as=style
Link: </common.js>; rel=preload; as=script

稍后:

200 OK
Content-Length: 7531
Content-Type: text/html; charset=UTF-8
Content-encoding: br
Link: <https://fonts.google.com>; rel=preconnect
Link: </main.css>; rel=preload; as=style
Link: </common.js>; rel=preload; as=script
Link: </experimental.3eab3290.css>; rel=preload; as=style
<HTML>
<head>
   <title>Example</title>
   <link rel="stylesheet" href="/main.css">
   <link rel="stylesheet" href="/experimental.3eab3290.css">
   <script src="/common.js"></script>
   <link rel="preconnect" href="https://fonts.googleapis.com">

浏览器支持

尽管所有主流浏览器都支持 103 Early Hints,但可在 Early Hint 中发送的指令因浏览器而异:

预连接支持

浏览器支持

  • 103
  • 103
  • 120
  • 17

预加载支持

浏览器支持

  • 103
  • 103
  • 123
  • x

Chrome 开发者工具还支持 103 Early Hints 支持,并且 Link 标头可以在文档资源中看到:

显示 Early Hints 标头的 Network 面板
早期提示 Link 标头显示在 Chrome 开发者工具中。

请注意,若要使用 Early Hints 资源,不得在开发者工具中勾选 Disable cache,因为 Early Hints 使用浏览器缓存。对于预加载的资源,启动器会显示为 early-hints大小会显示为 (Disk cache)

显示 Early Hints 发起者的网络面板
早期提示资源有一个 early-hints 启动器,并从磁盘缓存中加载。

这对于 HTTPS 测试也需要受信任的证书。

Firefox(自 v126 起)在开发者工具中没有明确的 103 Early Hints 支持,但使用 Early Hints 加载的资源不会显示 HTTP 标头信息,这是通过 Early Hints 加载的资源的一个指示符。

服务器支持

下面简要概述了热门开源软件 HTTP 服务器软件对 Early Hints 的支持级别:

更轻松地启用“早期提示”

如果您使用的是以下某个 CDN 或平台,则可能不需要手动实现早期提示。请参阅解决方案提供商的在线文档,了解它是否支持早期提示,或者参阅此处的非详尽列表:

如何避免不支持 Early Hints 的客户端出现问题

100 范围之内的信息性 HTTP 响应是 HTTP 标准的一部分,但是一些较旧的客户端或漫游器可能难以响应这些响应,因为在 103 早期提示 (Early Hints) 推出之前,它们很少用于常规的网页浏览。

只有在响应发送 sec-fetch-mode: navigate HTTP 请求标头的客户端时,才发出 103 个 Early Hints 应确保仅针对知道等待后续响应的较新客户端发送此类提示。此外,由于 Early Hints 仅在导航请求(请参阅当前限制)中支持,这也有一个额外的好处,即可以避免在其他请求中不必要地发送此类请求。

此外,建议仅通过 HTTP/2 或 HTTP/3 连接发送早期提示,大多数浏览器只会通过这些协议接受它们。

高级模式

如果您已将“早期提示”功能全面应用于关键着陆页,并且发现自己在寻求更多机会,那么您可能对以下高级模式感兴趣。

对于在典型用户体验历程中正在进行第 n 个页面请求的访问者,您可能需要调整 Early Hints 响应以适应页面中更靠下更深的内容,换句话说,就是对优先级较低的资源使用 Early Hints。nth这可能听起来不合常理,因为我们建议专注于高优先级、会阻塞渲染的子资源或源。不过,当访问者浏览一段时间时,他们的浏览器很可能已经拥有所有关键资源。此后,您就可以将注意力转移到优先级较低的资源上。例如,这可能意味着使用“早期提示”来加载产品图片,或者使用仅不常见的用户互动所需的其他 JS/CSS。

当前限制

在 Chrome 中实现的“早期提示”功能存在以下局限性:

  • 仅适用于导航请求(即顶级文档的主要资源)。
  • 仅支持 preconnectpreload(即不支持 prefetch)。
  • 如果使用 Early Hints,然后在最终响应上执行跨源重定向,将导致 Chrome 丢弃使用 Early Hints 获取的资源和连接。
  • 使用 Early Hints 预加载的资源存储在 HTTP 缓存中,稍后由页面从缓存中检索。因此,只有可缓存的资源才能使用 Early Hints 预加载,否则系统将重复提取该资源(由 Early Hints 和文档再次提取)。在 Chrome 中,系统会停用不受信任的 HTTPS 证书的 HTTP 缓存(即使您继续加载网页)。

其他浏览器具有类似的限制,如前所述,有些浏览器进一步将 103 早期提示限制为仅限 preconnect

后续操作

根据社区的兴趣,我们可能会通过以下功能来增强 Early Hints 的实现:

  • 有关使用内存缓存(而非 HTTP 缓存)不可缓存资源的早期提示。
  • 针对子资源请求发送的早期提示。
  • 针对 iframe 主要资源请求发送的早期提示。
  • 支持在 Early Hints 中预提取。

我们欢迎您提出建议,告诉我们应该优先考虑哪些方面,以及如何进一步改进 Early Hints。

与 H2/Push 的关系

如果您熟悉已弃用的 HTTP2/Push 功能,可能会想了解 Early Hints 有何不同。虽然 Early Hints 要求浏览器开始提取关键子资源的过程,但使用 HTTP2/Push 时,服务器可以在响应的同时开始推送子资源。虽然这听起来很棒,但这带来了一个关键的结构缺点:使用 HTTP2/Push 时,很难避免推送浏览器已有的子资源。这种“过度推送”效应导致网络带宽使用效率低下,严重阻碍了性能优势。总体而言,Chrome 数据表明,HTTP2/Push 实际上会对整个网络的性能造成负面影响。

相比之下,Early Hints 在实践中的性能更好,因为它将发送初步响应的能力与让浏览器负责提取或连接到实际所需内容的提示相结合。虽然 Early Hints 并未涵盖理论上 HTTP2/Push 可以处理的所有用例,但我们认为 Early Hints 是更实用的加快导航速度的解决方案。

缩略图由 Pierre Bamin 拍摄。