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

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

肯吉·巴赫
Kenji Baheux
巴里·波拉德
Barry Pollard

什么是早期提示?

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

图片:服务器认为网页加载和加载其他资源之间相差 200 毫秒。
如果没有 Early Hints:在确定如何响应主资源时,服务器会阻止所有内容。

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

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

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

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

实现早期提示

在深入探讨这个主题之前,请注意,如果您的服务器可以立即发送 200(或其他最终响应),那么早期提示就没什么用了。在这种情况下,不妨考虑对主响应(Link rel HTTP 标头)或主响应(<link> 元素)使用常规的 link rel=preloadlink rel=preconnect。如果您的服务器需要一些时间来生成主响应,请继续阅读!

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

现在,您已经拥有了这个优先着陆页列表,接下来就是确定哪些来源或子资源适合使用预连接预加载提示(作为第一个近似值)。通常情况下,对关键用户指标(如 Largest Contentful PaintFirst Contentful Paint)影响最大的源和子资源。具体而言,查找会阻止内容呈现的子资源,例如同步 JavaScript、样式表,甚至是网页字体。同样,应查找托管有子资源(对关键用户指标有很大影响)的来源。注意:如果您的主要资源已在使用 <link rel=preconnect><link rel=preload>,您可以考虑这些来源或资源来申请 Early Hints。如需了解详情,请参阅这篇文章

第二步是尽可能降低对可能已过时或不再由主资源使用的资源或源使用 Early Hint 的风险。例如,频繁更新和版本控制的资源(例如 example.com/css/main.fa231e9c.css)可能不是最佳选择。请注意,此问题并非特定于早期提示,它适用于任何可能存在的 rel=preloadrel=preconnect 链接。对于此类细节而言,是最适合自动化或模板化处理的(例如,手动流程更有可能会导致 link rel=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
  • x

服务器支持

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

启用早期提示,便捷易用

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

避免让不支持 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。n这可能听起来有悖常理,因为我们建议将侧重点放在高优先级、阻塞渲染的子资源或来源上。但是,经过一段时间访问者的浏览,他们的浏览器很可能已经拥有了所有的关键资源。之后,您便可以将注意力转移到优先级较低的资源上。例如,这可能意味着使用 Early Hints 加载商品图片,或仅使用不太常见的用户互动所需的其他 JS/CSS。

当前的局限性

以下是在 Chrome 中实现的早期提示存在的限制:

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

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

后续操作

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

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

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

与 H2/推送的关系

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

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

缩略图,提供者:Pierre Bamin