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

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

Kenji Baheux
Kenji Baheux

什么是“早期提示”?

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

该图片显示了服务器在网页加载与其他资源加载之间相差 200 毫秒。
不使用 Early Hints:确定如何响应主要资源时,服务器上的所有内容都会被阻止。

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

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

在某些情况下,Largest Contentful Paint 的性能改进可能从几百毫秒开始(如 ShopifyCloudflare 所观察到的那样),最多可能缩短一秒,如对比前后所示:

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

如何使用早期提示

若要利用 Early Hints 功能,第一步包括确定热门着陆页,也就是用户访问您的网站时通常首先进入的网页。这可以是首页或热门商品详情页面(如果您的大量用户来自其他网站)。这些入口点比其他网页更为重要的原因在于,当用户浏览您的网站时,Early Hints 的实用性会降低(也就是说,浏览器在第二次或第三次后续导航时更有可能拥有所需的所有子资源)。给人留下良好的第一印象也不失为一个好主意!

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

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

在 HTML 中使用这些元素时,您通常需要 preconnectpreload 预加载扫描程序不会在 HTML 中发现的资源(例如,原本会延迟发现的字体或背景图片)。对于 Early Hints,您没有 HTML,因此可能想对关键网域或 preload 关键资源执行 preconnect 操作,而这些关键资源原本可能会在 HTML 的早期发现,例如预加载 main.cssapp.js。此外,并非所有浏览器都支持对 Early Hints 使用 preload。请参阅浏览器支持

第二步是尽量降低对可能已过时或不再由主要资源使用的资源或源使用 Early Hints 的风险。例如,频繁更新和版本控制的资源(例如 example.com/css/main.fa231e9c.css)可能不是最佳选择。请注意,此问题并非特定于 Early Hints,而是适用于任何可能存在的 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) 领先 5 个版本 (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">

最后,在服务器端,查找已知支持 Early Hints 的浏览器发送的主要资源请求,并立即响应 103 Early Hints。在 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 支持

服务器支持

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

更轻松地启用早期提示

如果您使用的是以下 CDN 或平台之一,则可能不需要手动实现 Early Hints。请参阅解决方案提供商的在线文档以了解其是否支持 Early Hints,或参阅此处的非完整列表:

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

范围为 100 的信息性 HTTP 响应是 HTTP 标准的一部分,但一些较旧的客户端或漫游器可能无法应对此类响应,因为在 103 Early Hints 发布之前,这类响应已很少用于一般的网页浏览。

仅在响应发送 sec-fetch-mode: navigate HTTP 请求标头的客户端时发出 103 条 Early Hints 应确保只针对理解等待后续响应的较新客户端发送此类提示。此外,由于“提前提示”仅适用于导航请求(请参阅当前限制),因此它的一个额外好处是可以避免在其他请求中不必要地发送此类提示。

此外,建议仅通过 HTTP/2 或 HTTP/3 连接发送 Early Hint

高级模式

如果您已将 Early Hints 完全应用于关键着陆页,并发现自己在寻找更多机会,您可能会对以下高级模式感兴趣。

对于在典型用户体验历程中收到第 n 个网页请求的访问者,您可能需要调整早期提示响应,以适应页面中位置更深、更深的内容,也就是说,对优先级较低的资源使用早期提示。nth这可能听起来不合常理,因为我们建议将重点放在高优先级、阻碍呈现的子资源或源站上。不过,在访问者浏览一段时间后,其浏览器很可能已经获取了所有关键资源。此后,您可能会将注意力转移到优先级较低的资源上。例如,这可能意味着使用 Early Hints 加载商品图片,或者使用仅在不太常见的用户互动时所需的其他 JS/CSS。

当前限制

以下是在 Chrome 中实现的 Early Hints 的限制:

  • 仅适用于导航请求(即顶级文档的主要资源)。
  • 仅支持 preconnectpreload(即不支持 prefetch)。
  • 提前提示,后跟对最终响应的跨源重定向,会导致 Chrome 丢弃使用 Early Hints 获取的资源和连接。

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

后续操作

根据社区的兴趣,我们可能会在实现 Early Hints 后加强以下功能:

  • 针对子资源请求发送的早期提示。
  • 在 iframe 主资源请求中发送的早期提示。
  • 支持在 Early Hints 中预提取。

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

与 H2/Push 的关系

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

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

缩略图提供者:Pierre Bamin