优化加载第一个字节所需时间

了解如何针对“加载第一个字节所需时间”指标进行优化。

Jeremy Wagner
Jeremy Wagner
Barry Pollard
Barry Pollard

首次传输字节时间 (TTFB) 是一项基础的 Web 性能指标,位于所有其他有意义的用户体验指标(例如 First Contentful Paint [FCP]Largest Contentful Paint [LCP])之前。也就是说,TTFB 值越高,后续指标所需的时间就越长。

建议您的服务器快速响应导航请求,以便75 百分位数的用户获得在“良好”阈值范围内的 FCP。作为粗略的指导,大多数网站应力求 TTFB 不超过 0.8 秒

TTFB 值在 0.8 秒或更短时间内为良好值,大于 1.8 秒为较差值,介于二者之间的值则需要改进

如何衡量 TTFB

在优化 TTFB 之前,您需要观察它对网站用户的影响。您应将现场数据作为观察 TTFB 的主要来源,因为它会受到重定向的影响,而实验室工具通常使用最终网址进行衡量,因此会忽略这段额外的延迟时间。

PageSpeed Insights 是获取 Chrome 用户体验报告中提供的公开网站的实测和实验室信息的一种方式。

真实用户的 TTFB 显示在顶部的了解您的真实用户的体验部分:

PageSpeed Insights 实际用户数据,包括 TTFB 指标的现场数据。

服务器响应时间审核中会显示 TTFB 的一部分:

服务器响应时间审核

如需了解在现场和实验室中衡量 TTFB 的更多方法,请参阅 TTFB 指标页面

使用 Server-Timing 调试字段中的高 TTFB

您可以在应用后端使用 Server-Timing 响应标头来衡量可能导致延迟时间较长的各个后端进程。标头值的结构非常灵活,至少接受您定义的句柄。可选值包括时长值(通过 dur)以及可选的直观易懂的说明(通过 desc)。

Serving-Timing 可用于衡量许多应用后端进程,但您可能需要���别注意以下进程:

  • 数据库查询
  • 服务器端呈现时间(如果适用)
  • 磁盘跳转
  • 边缘服务器缓存命中/未命中次数(如果使用 CDN)

Server-Timing 条目的所有部分均以英文冒号分隔,多个条目可以用英文逗号分隔:

// Two metrics with descriptions and values
Server-Timing: db;desc="Database";dur=121.3, ssr;desc="Server-side Rendering";dur=212.2

您可以使用应用后端的首选语言设置标头。例如,在 PHP 中,您可以按如下方式设置标头:

<?php
// Get a high-resolution timestamp before
// the database query is performed:
$dbReadStartTime = hrtime(true);

// Perform a database query and get results...
// ...

// Get a high-resolution timestamp after
// the database query is performed:
$dbReadEndTime = hrtime(true);

// Get the total time, converting nanoseconds to
// milliseconds (or whatever granularity you need):
$dbReadTotalTime = ($dbReadEndTime - $dbReadStartTime) / 1e+6;

// Set the Server-Timing header:
header('Server-Timing: db;desc="Database";dur=' . $dbReadTotalTime);
?>

设置此标头后,系统会显示您可以在实验实际场景中使用的相关信息。

在该字段中,设置了 Server-Timing 响应标头的任何网页都会在 Navigation Timing API 中填充 serverTiming 属性:

// Get the serverTiming entry for the first navigation request:
performance.getEntries('navigation')[0].serverTiming.forEach(entry => {
  // Log the server timing data:
  console.log(entry.name, entry.description, entry.duration);
});

在本实验中,Chrome DevTools 的 Network(网络)标签页中的 Timings(时间)面板中会直观显示 Server-Timing 响应标头中的数据:

Chrome DevTools 的“Network”(网络)标签页中 Server-Timing 标头值的可视化结果。在此图片中,Server-Timing 标头值用于衡量 CDN 边缘服务器是否命中缓存,以及从边缘服务器和源服务器检索资源所用的时间。

Server-Timing 响应标头在 Chrome 开发者工具的“Network”(网络)标签页中可视化显示。在这里,Server-Timing 用于衡量对资源的请求是否命中了 CDN 缓存,以及请求命中 CDN 边缘服务器和源服务器所需的时间。

通过分析可用数据确定 TTFB 存在问题后,您就可以着手解决问题了。

优化 TTFB 的方法

优化 TTFB 最具挑战性的方面在于,虽然网站的前端堆栈始终是 HTML、CSS 和 JavaScript,但后端堆栈可能差异很大。有许多后端堆栈和数据库产品,每种产品都有自己的优化技术。因此,本指���将重点介绍适用于大多数架构的内容,而不会仅关注特定于堆栈的指导。

平台专用指南

您为网站使用的平台可能会严重影响 TTFB。例如,WordPress 性能会受到插件数量和质量或所用主题的影响。自定义平台后,其他平台也会受到类似影响。您应参阅平台文档,了解特定于供应商的建议,以补充本文中更笼统的效果建议。用于缩短服务器响应时间的 Lighthouse 审核还包含一些特定于堆栈的有限指南

Hosting, hosting, hosting

在考虑其他优化方法之前,您应该先考虑托管服务。我们无法在此提供太多具体的指导,但一般来说,您应确保网站的托管服务商能够处理您发送给它的流量。

共享托管服务的速度通常较慢。如果您运营的是一个小型个人网站,并且主要提供静态文件,那么这可能没什么问题。下面介绍的一些优化技巧有助于您尽可能缩短 TTFB。

不过,如果您运行的应用较大,用户众多,并且涉及个性化、数据库查询和其他密集型服务器端操作,那么您选择的托管服务对于降低现场 TTFB 至关重要。

选择托管服务提供商时,请注意以下事项:

  • 您的应用实例分配了多少内存?如果应用的内存不足,则会发生过载,并难以尽快传送网页。
  • 您的托管服务提供商是否会及时更新您的后端堆栈?随着应用后端语言、HTTP 实现和数据库软件的新版本发布,这些软件的性能会随着时间的推移而不断提升。因此,请务必与将此类重要维护工作列为优先事项的托管服务提供商合作。
  • 如果您有非常具体的应用要求,并且希望获得对服务器配置文件的最低级别访问权限,请考虑是否有必要自定义您自己的应用实例的后端。

许多托管服务提供商都会为您处理这些问题,但如果您开始发现 TTFB 值很长(即使是使用专用托管服务提供商),则可能需要重新评估您当前托管服务提供商的能力,以便提供尽可能出色的用户体验。

使用内容分发网络 (CDN)

CDN 使用是一个老生常谈的话题,但原因很充分:即使您的应用后端经过了非常好的优化,但位于距离您的源服务器较远的用户在现场可能仍会遇到较高的 TTFB。

CDN 通过使用分布式服务器网络来解决用户与源服务器之间的距离问题,该网络会在距离用户更近的服务器上缓存资源。这些服务器称为“边缘服务器”

CDN 提供商还可能提供边缘服务器之外的优势:

  • CDN 提供商通常可提供极快的 DNS 解析时间。
  • CDN 可能会使用 HTTP/2 或 HTTP/3 等新型协议从边缘服务器分发您的内容。
  • 特别是,HTTP/3 通过使用 UDP 协议解决了 TCP(HTTP/2 依赖的协议)中存在的首个请求阻塞问题。
  • CDN 可能还会提供新型 TLS 版本,从而缩短 TLS 协商时间所涉及的延迟时间。特别是 TLS 1.3 旨在尽可能缩短 TLS 协商时间。
  • 某些 CDN 提供商提供的功能通常称为“边缘工作器”,它使用与 Service Worker API 类似的 API 来拦截请求、以编程方式管理边缘缓存中的响应,或完全重写响应。
  • CDN 提供商非常擅长进行压缩优化。压缩很难自行正确完成,在某些情况下,如果使用动态生成的标记(必须动态压缩),可能会导致响应时间变慢。
  • CDN 提供商还会自动缓存静态资源的压缩响应,从而实现最佳压缩比和响应时间组合。

虽然采用 CDN 需要付出不同程度的努力(从微不足道到非常显著),但如果您的网站尚未使用 CDN,则应优先考虑优化 TTFB。

尽可能使用缓存内容

借助 CDN,您可以将内容缓存在距离访问者更近的边缘服务器上,前提是内容配置了适当的 Cache-Control HTTP 标头。虽然这不适用于个性化内容,但如果需要一直返回到源站,CDN 的许多价值可能会被抵消。

对于频繁更新内容的网站,即使缓存时间很短,对于繁忙的网站来说,也能显著提升性能,因为在此期间只有第一位访问者会经历返回源服务器的完整延迟时间,而所有其他访问者都可以重复使用边缘服务器中的缓存资源。某些 CDN 允许在网站发布时使缓存失效,从而实现两全其美:缓存时间较长,但在需要时可立即更新。

即使缓存配置正确,您也可以通过使用唯一的查询字符串参数来衡量分析数据,从而忽略此问题。虽然它们是相同的内容,但在 CDN 看来可能像是不同的内容,因此系统不会使用缓存的版本。

系统可能也不会缓存较旧或访问次数较少的内容,这可能会导致某些网页的 TTFB 值高于其他网页。延长缓存时间可以降低这种影响,但请注意,延长缓存时间会增加提供可能过时内容的可能性。

缓存内容的影响不仅仅局限于使用 CDN 的用户。当缓存的内容无法重复使用时,服务器基础架构可能需要通过耗费大量资源的数据库查询来生成内容。更频繁访问的数据或预缓存的网页通常性能更好。

避免多次重定向网页

导致 TTFB 较高的一个常见原因是重定向。当文档的导航请求收到一条告知浏览器资源位于其他位置的响应时,就会发生重定向。一次重定向肯定会给导航请求增加不必要的延迟时间,但如果该重定向指向另一个资源,而该资源又导致另一次重定向,情况肯定会更糟糕。这对通过广告或简报吸引大量访问者的网站影响尤为明显,因为这些网站通常会出于衡量目的通过分析服务进行重定向。消除您直接控制的重定向有助于实现良好的 TTFB。

重定向分为以下两种类型:

  • 同源重定向,即重定向���全在您的网站上发生。
  • 跨源重定向:在这种情况下,重定向最初发生在另一个源(例如社交媒体网址缩短服务)上,然后才到达您的网站。

您需要重点消除同源重定向,因为您可以直接控制这一点。这需要检查您网站上的链接,看看其中是否有任何链接会导致 302301 响应代码。这通常是由于未添加 https:// 架构(因此浏览器会默认使用 http://,然后重定向),或者由于网址中未正确添加或排除尾部斜线。

跨源重定向更难处理,因为这些重定向通常超出您的控制范围,但请尽可能避免多次重定向,例如在分享链接时使用多个链接缩短工具。确保向广告客户或简报提供的网址是正确的最终到达网址,以免在这些服务使用的重定向网址中再添加重定向。

重定向时间的另一个重要来源可能是 HTTP 到 HTTPS 重定向。解决此问题的一种方法是使用 Strict-Transport-Security 标头 (HSTS),该标头会在首次访问某个源时强制使用 HTTPS,然后会告知浏览器在日后访问该源时立即通过 HTTPS 方案访问。

制定了适当的 HSTS 政策后,您可以将您的网站添加到 HSTS 预加载列表,加快首次访问某个源的速度。

将标记流式传输到浏览器

浏览器经过优化,可在流式传输时高效处理标记,这意味着,标记会在从服务器传入时分块处理。对于大型标记载荷,这一点至关重要,因为这意味着浏览器可以增量解析这些标记块,而不是等待整个响应到达后才能开始解析。

虽然浏览器非常擅长处理流式标记,但请务必尽一切可能让流式持续传输,以便这些初始标记尽快传输。如果后端出现问题,就会导致延迟。由于后端堆栈众多,因此本指南无法涵盖每种堆栈以及每种特定堆栈中可能出现的问题。

例如,React 以及其他可以在服务器上按需呈现标记的框架都采用了同步服务器端呈现方法。不过,较新版本的 React 在呈现标记时实现了用于流式传输标记的服务器方法。这意味着,您无需等待 React 服务器 API 方法渲染整个响应,即可发送响应。

确保将标记快速流式传输到浏览器的另一种方法是依赖于静态渲染,该方法会在构建时生成 HTML 文件。由于完整文件立即可用,因此网络服务器可以立即开始发送文件,而 HTTP 的固有特性将导致流式标记。虽然此方法不适用于每个网站上的每个网页(例如需要在用户体验中提供动态响应的网页),但对于不需要针对特定用户进行标记个性化的网页,此方法会很有用。

使用服务工件

Service Worker API 可能会对文档及其加载的资源的 TTFB 产生重大影响。原因在于,服务工件充当浏览器和服务器之间的代理,但对您网站的 TTFB 有何影响取决于您设置服务工件的方式,以及该设置是否符合您的应用要求。

  • 为资产使用“过时时重新验证”策略如果资源位于 Service Worker 缓存中(无论是文档还是文档所需的资源),“在重新验证期间使用过时资源”策略会从缓存中提供该资源,然后在后台下载该资源,并在日后互动时从缓存中提供该资源。
    • 如果您有文档资源不经常更改,使用“在重新验证期间使用过时数据”策略可以使网页的 TTFB 几乎为零。不过,如果您的网站发送的是动态生成的标记(例如,根据用户是否已通过身份验证而发生变化的标记),这种方法的效果就不太理想。在这种情况下,您始终应访问网络,以便文档尽可能保持最新状态。
    • 如果您的文档加载的是会频繁更改但提取过时资源不会对用户体验产生很大影响的非关键资源(例如某些图片或其他非关键资源),则可以使用“在提取时重新验证”策略大幅缩短这些资源的 TTFB。
  • 为客户端呈现的应用使用应用 Shell 模型此模型最适合 SPA,在这种情况下,页面的“壳”可以从服务工件缓存中立即提交,而页面的动态内容会在页面生命周期的后续阶段填充和呈现。

针对渲染至关重要的资源使用 103 Early Hints

无论应用后端的优化程度如何,服务器可能仍需要执行大量工作来准备响应,包括耗时(但必要)的数据库工作,这会延迟导航响应的到达时间。这可能会导致一些后续的渲染关键资源延迟,例如 CSS,或者在某些情况下,在客户端上渲染标记的 JavaScript。

103 Early Hints 标头是一种早期响应代码,服务器可以在后端忙于准备标记时将其发送给浏览器。此标头可用于提示浏览器,在准备标记时,网页应开始下载渲染至关重要的资源。对于支持的浏览器,效果可能是文档渲染速度更快 (CSS),并且核心网页功能可更快使用 (JavaScript)。

总结

由于后端应用堆栈的组合非常多,因此没有一篇文章可以概括您可以采取的所有措施来缩短网站的 TTFB。不过,您可以考虑以下一些选项,尝试让服务器端的速度略微加快一些。

与优化每个指标一样,方法大致相同:在现场测量 TTFB,使用实验室工具深入探究原因,然后尽可能应用优化措施。这里的每种方法可能并不适用于您的情况,但其中有些方法可能适用。一如既往,您需要密切关注现场数据,并根据需要进行调整,以确保尽可能快地为用户提供良好体验。

主打图片由 Taylor Vick 拍摄,来自 Unsplash。