如何衡量和优化 Signed Exchange,充分提升其效果
Signed Exchange (SXG) 是一种提升网页速度的方法,主要是 Largest Contentful Paint (LCP)。在引荐网站(目前为 Google 搜索)链接指向网页时,这些链接可在用户点击链接之前预提取到浏览器缓存中。
您可以构建这样的网页:在预提取时,呈现网页的关键路径上不需要网络!使用 4G 连接时,网页加载时间从 2.8 秒缩短到 0.9 秒(其余 0.9 秒主要取决于 CPU 使用情况):
如今,发布 SXG 的大多数人都在使用 Cloudflare 简单易用的 Automatic Signed Exchanges (ASX) 功能(但也有开源方案):
在许多情况下,选中相应复选框来启用此功能就足以实现上述实质性改进。有时,您还需要执行几个步骤,以确保这些 SXG 在流水线的每个阶段按预期运行,并优化页面以充分利用预提取。
自 Cloudflare 推出以来的几个月里,我一直在各种 论坛上阅读和回复问题,并学习如何向网站提供关于如何确保充分利用 SXG 部署的建议。这篇博文汇集了我的建议。我将向您详细介绍相关步骤:
- 使用 WebPageTest 分析 SXG 性能。
- 如果“分析”步骤显示它无法正常运行,请调试 SXG 流水线。
- 针对 SXG 预提取优化页面,包括设置最佳
max-age
和预加载阻止渲染的子资源。 - 选择合适的实验组和对照组,使用 Google Analytics(分析)衡量 SXG 改进。
简介
SXG 是包含网址、一组 HTTP 响应标头和响应正文的文件,均由 Web PKI 证书进行加��签名。浏览器加载 SXG 时,会验证以下所有项:
- SXG 尚未到期。
- 签名与网址、标头、正文和证书相匹配。
- 证书有效且与网址匹配。
如果验证失败,浏览器将放弃 SXG,改为提取已签名网址。如果验证成功,浏览器就会加载带签名的响应,将其视为直接来自该签名网址。这样就可以将 SXG 重新托管在任何服务器上,只要服务器在签名后未过期或未发生修改即可。
对于 Google 搜索,SXG 支持在其搜索结果中预提取网页。对于支持 SXG 的网页,Google 搜索可以预提取其缓存的网页副本,该副本托管在 webpkgcache.com 上。这些 webpkgcache.com 网址不会影响页面的显示或行为,因为浏览器会遵循原始的签名网址。预提取功能可以大大加快网页的加载速度。
分析
如需了解 SXG 的优势,请先使用实验室工具分析 SXG 在可重复条件下的性能。您可以使用 WebPageTest 比较在使用 SXG 预提取时和不使用 SXG 预提取时的瀑布流和 LCP。
生成不使用 SXG 的测试,如下所示:
- 转到 WebPageTest 并登录。登录后,系统会保存您的测试历史记录,以便您日后更轻松地进行比较。
- 输入要测试的网址。
- 转到高级配置。(您将需要使用高级配置进行 SXG 测试,因此在此处使用高级配置有助于确保测试选项相同。)
- 在 Test Settings(测试设置)标签页中,将“Connection”(连接)设置为 4G 并将“Number of Tests to Run”(要运行的测试数)增加到 7 可能会有所帮助。
- 点击开始测试。
按照与上述步骤相同的步骤,使用 SXG 生成测试,但在点击 Start Test 之前,请前往 Script 标签页,粘贴以下 WebPageTest 脚本,然后按照指示修改两个 navigate
网址:
// Disable log collection for the first step. We only want the waterfall for the target navigation.
logData 0
// Visit a search result page that includes your page.
navigate https://google.com/search?q=site%3Asigned-exchange-testing.dev+image
// Wait for the prefetch to succeed.
sleep 10
// Re-enable log collection.
logData 1
// Navigate to the prefetched SXG on the Google SXG Cache.
navigate https://signed--exchange--testing-dev.webpkgcache.com/doc/-/s/signed-exchange-testing.dev/sxgs/valid-image-subresource.html
对于第一个 navigate
网址,如果您的网页尚未显示在任何 Google 搜索结果中,您可以使用此预提取网页生成用于此目的的假想搜索结果页。
要确定第二个 navigate
网址,请使用 SXG 验证工具 Chrome 扩展程序访问您的网页,然后点击扩展程序图标以查看缓存网址:
完成这些测试后,前往 Test History(测试历史记录),选择这两个测试,然后点击 Compare:
将 &medianMetric=LCP
附加到比较网址,以便 WebPageTest 为比较的两侧选择 LCP 中间值运行的运行。(默认值为速度指数的中位数)。
如需比较瀑布流,请展开 Waterfall Opacity(瀑布透明度)部分,然后拖动滑块。要观看视频,请点击调整幻灯影片设置,在该对话框中向下滚动,然后点击观看视频。
如果 SXG 预提取成功,您会看到“with SXG”广告瀑布流不包含 HTML 行,并且对子资源的提取会更早开始。例如,下面是比较“之前”和“之后”的对比:
调试
如果 WebPageTest 显示正在预提取 SXG,则表明它已成功完成流水线的所有步骤;您可以跳至优化部分,了解如何进一步改进 LCP。否则,您需要找出流水线中哪个位置出现了故障以及失败原因;请继续阅读下文,了解操作方法。
正在发布
确保您的网页是作为 SXG 生成的。为此,您需要冒充抓取工具。最简单的方法是使用 SXG 验证工具 Chrome 扩展程序:
该扩展程序会提取当前网址,其中的 Accept
请求标头表明其首选 SXG 版本。如果“源”旁边出现对勾标记 (✅),则表示系统返回了 SXG;您可以跳至编入索引部分。
如果您看到叉号 (❌),则表示 SXG 未退货:
如果 Cloudflare ASX 已启用,则出现十字标记 (❌) 的最可能原因是缓存控制响应标头阻止了它。ASX 会检查具有以下名称的标头:
Cache-Control
CDN-Cache-Control
Surrogate-Control
Cloudflare-CDN-Cache-Control
如果其中任何标头包含以下任一标头值,都会阻止生成 SXG:
private
no-store
no-cache
max-age
小于 120(除非被大于或等于 120 的s-maxage
替换)
在这种情况下,ASX 不会创建 SXG,因为 SXG 可能会被缓存并重复利用给多次访问和多个访问者使用。
出现十字标记 (❌) 的另一个可能原因是存在这些有状态响应标头(Set-Cookie
除外)。ASX 移除了 Set-Cookie
标头,以符合 SXG 规范。
另一个可能的原因是存在 Vary: Cookie
响应标头。Googlebot 在没有用户凭据的情况下抓取 SXG,并可能会将 SXG 提供给多个访问者。如果您根据不同用户的 Cookie 为其提供不同的 HTML,则他们可能会看到错误的体验,例如退出登录。
除了 Chrome 扩展程序,您还可以使用 curl
等工具:
curl -siH "Accept: application/signed-exchange;v=b3" $URL | less
dump-signedexchange -verify -uri $URL
如果该 SXG 存在且有效,您将看到 SXG 的直观易读输出。否则,您将会看到一条错误消息。
索引编制
确保您的 SXG 已被 Google 搜索成功编入索引。打开 Chrome 开发者工具,然后对您的网页执行 Google 搜索。如果该网页已被作为 SXG 编入索引,那么 Google 指向您网页的链接将包含一个指向 webpkgcache.com 副本的 data-sxg-url
:
如果 Google 搜索认为用户可能会点击结果,还会预提取该结果:
<link>
元素指示浏览器将 SXG 下载到其预提取缓存中。当用户点击 <a>
元素时,浏览器将使用缓存的 SXG 来呈现页面。
您也可以前往开发者工具中的“Network”标签页,搜索包含 webpkgcache
的网址,查看预提取的证据。
如果 <a>
指向 webpkgcache.com,这意味着 Signed Exchange 的 Google 搜索索引编制功能正常。您可以跳到 Ingestion(提取)部分。
否则,可能是因为在您启用 SXG 后,Google 尚未重新抓取您的网页。不妨试用 Google Search Console 网址检查工具:
digest: mi-sha256-03=...
标头的存在表示 Google 已成功抓取 SXG 版本。
如果 digest
标头不存在,则可能表示 SXG 未提供给 Googlebot,或者自您启用 SXG 后索引尚未更新。
如果 SXG 已成功抓取,但仍未关联,则可能不符合 SXG 缓存要求。这些内容将在下一部分进行介绍。
注入
当 Google 搜索将 SXG 编入索引时,它会将其副本发送到 Google SXG Cache,后者会根据缓存要求对其进行验证。Chrome 扩展程序会显示结果:
如果您看到对勾标记 (✅),则可以直接跳转到优化。
如果不符合要求,您会看到叉号 (❌) 和警告消息,说明原因:
在这种情况下,该页面将像启用 SXG 之前一样正常运行。Google 会链接到其原始主机上的相应网页,而不会预提取 SXG 内容。
如果缓存副本���������并在后���重新提取,您会看到一个沙漏 (⌛):
SXG 上的 Google 开发者文档还包含如何手动查询缓存的说明。
优化
如果 SXG 验证工具 Chrome 扩展程序显示所有对勾标记 (✅),则表示您有一个 SXG 可以向用户投放!请继续阅读下文,了解如何优化网页,以便通过 SXG 最大限度地提升 LCP 效果。
max-age
当 SXG 过期后,Google SXG Cache 将在后台提取新副本。在等待提取期间,系统会将用户定向到该网页的原始主机(非预提取)。设置 Cache-Control: max-age
的时间越长,此后台提取的发生频率就越低,因此预提取可以越频繁地减少 LCP。
这是性能和新鲜度之间的权衡,缓存允许网站所有者为 SXG 提供 2 分钟到 7 天的最长存在时间,以满足每个页面的特定需求。有趣的是,我们发现:
max-age=86400
(1 天)或更长时间更有助于提升效果max-age=120
(2 分钟)不
随着对数据的研究,我们希望可以对两者之间的值有所了解。
user-agent
有一次,我看到在使用预提取 SXG 时 LCP 增加。我运行了 WebPageTest,比较不使用 SXG 预提取和使用 SXG 预提取时的中位数结果。点击下方的之后:
我发现预提取功能有效。HTML 从关键路径中移除,因此所有子资源都能提前加载。但 LCP(绿色虚线)从 2 秒增加到了 2.1 秒。
为了进行诊断,我查看了胶卷。我发现网页在 SXG 中的呈现方式不同。在纯 HTML 中,Chrome 确定 LCP 的“最大元素”是标题。然而,在 SXG 版本中,页面添加了延迟加载的横幅广告,这会将标题推到非首屏位置,并将新的最大元素设为延迟加载 Cookie 意见征求对话框。所有内容的呈现速度都比以前更快,但布局的变化导致该指标报告的显示速度变慢。
经过深入分析,我发现布局差异的原因在于页面因 User-Agent
而异,并且逻辑中存在错误。尽管 SXG 抓取标头指明了移动版网页,但该网页投放的是桌面版网页。此问题解决后,浏览器再次将网页的标题正确识别为最大的元素。
现在,点击“之后”,我看到预提取的 LCP 下降到 1.3 秒:
SXG 适用于所有设备规格。为此,请确保满足以下任一条件:
- 您的网页没有按
User-Agent
进行Vary
(例如,它采用了自适应设计或单独的移动版/桌面版网址)。 - 如果您的网页采用动态提供内容,它会使用
<meta name=supported-media content=...>
将自身标注为仅限移动设备或桌面设备。
子资源
SXG 可用于预提取子资源(包括图片)以及 HTML。Cloudflare ASX 将扫描 HTML 中是否存在同源(第一方)<link rel=preload>
元素,并将其转换为与 SXG 兼容的链接标头。如需了解详情,请点击此处和此处。
如果抓取成功,您会看到 Google 搜索中的其他预提取内容:
若要针对 LCP 进行优化,请仔细查看广告瀑布流,并确定哪些资源位于渲染最大元素的关键路径上。如果无法预提取它们,请考虑是否可以将其从关键路径中移除。请留意有哪些脚本会在网页加载完成之前隐藏网页。
Google SXG Cache 允许预加载最多 20 个子资源,ASX 可确保不会超出此限制。但是,添加过多的子资源预加载存在风险。只有在所有预加载的子资源均已抓取完毕时,浏览器才会使用预加载的子资源,从而防止跨网站跟踪。子资源越多,所有子资源在用户点击到达您的网页前完成预提取的可能性越小。
SXG 验证工具目前不会检查子资源;如需进行调试,请同时使用 curl
或 dump-signedexchange
。
测量
在 WebPageTest 中优化 LCP 改进后,衡量 SXG 预提取对网站整体性能的影响会很有帮助。
服务器端指标
在衡量首字节时间 (TTFB) 等服务器端指标时,请务必注意,您的网站仅向接受 SXG 的抓取工具提供 SXG。将 TTFB 的衡量范围限制为来自真实用户(而非漫���器)的请求。您可能会发现,生成 SXG 会增加抓取工具请求的 TTFB,但这不会影响访问者的体验。
客户端指标
SXG 对客户端指标(尤其是 LCP)的速度优势最大。在衡量它们的影响时,您只需启用 Cloudflare ASX,等待 Googlebot 重新抓取它,再等待 28 天进行核心网页指标 (CWV) 汇总,然后查看新的 CWV 数据。但是,如果这种变化与这段时间内的所有其他变化混合在一起,可能就很难被发现。
相反,我发现“放大”可能受影响的网页加载,并将其构建为:“SXG 影响 X% 的网页浏览量,在第 75 个百分位处将 LCP 提高 Y 毫秒。”
目前,SXG 预提取仅在特定条件下进行:
- Chromium 浏览器(例如 Chrome 或 Edge,iOS 版除外),M98 或更高版本
Referer: google.com
或其他 Google 搜索网域。(请注意,在 Google Analytics(分析)中,引荐标记适用于会话中的所有网页浏览,而 SXG 预提取仅适用于 Google 搜索直接关联的首次网页浏览。)
请阅读“当代研究”部分,了解如何衡量“X% 的网页浏览量”和“将其 LCP 提升 Y 毫秒”。
当代研究
查看真实用户监控 (RUM) 数据时,您应将网页加载划分到 SXG 和非 SXG。这样做时,请务必限制您要查看的一系列网页加载,使非 SXG 端符合 SXG 的资格条件,以免发生选择性偏差。否则,以下所有项将仅存在于一组非 SXG 页面加载���,这些非 SXG 页面可能具有本质上不同的 LCP:
- iOS 设备:由于使用 iOS 设备的用户在硬件或网速方面存在差异。
- 旧版 Chromium 浏览器:原因相同。
- 桌面设备:出于相同原因,或者由于页面布局导致选择不同的“最大元素”。
- 同网站导航(访问者访问网站中的链接):因为它们可以重复使用上次网页加载时缓存的子资源。
在 Google Analytics(分析)(UA) 中,创建两个范围为“命中”的自定义维度,一个名为“isSXG”,另一个名为“referrer”。(内置的“来源”维度具有会话级范围,因此不排除同网站浏览。)
创建一个名为“SXGcounterfactual”的自定义细分,并将以下过滤条件以 AND 关系结合在一起:
referrer
开头是https://www.google.
- “
Browser
”与“Chrome
”完全匹配 Browser
版本与正则表达式^(9[8-9]|[0-9]{3})
匹配- “
isSXG
”与“false
”完全匹配
创建此细分的副本,将其命名为“SXG”,但参数 isSXG
与 true
完全匹配。
在网站模板中,在 Google Analytics(分析)代码段上方添加以下代码段。这是一种特殊语法,在生成 SXG 时,ASX 会将 false
更改为 true
:
<script data-issxg-var>window.isSXG=false</script>
根据建议自定义 Google Analytics(分析)报告脚本,以记录 LCP。如果您使用的是 gtag.js,请修改 'config'
命令以设置自定义维度(将 'dimension1'
和 'dimension2'
替换为 Google Analytics(分析)指定使用的名称):
gtag('config', 'YOUR_TRACKING_ID', {
'dimension1': String(isSXG),
'dimension2': document.referrer,
});
如果您使用的是 analytics.js,请按照此处所述修改 'create'
命令。
等待几天收集一些数据后,转到 Google Analytics(分析)“事件”报告,然后添加 SXG 细分的细目。这应该会填上“SXG 影响 X% 的网页浏览量”的 X:
最后,前往网页指标报告,选择“选择细分”,然后选择“SXGcounterfactual”和“SXG”。
点击“Submit”(提交),您应该会看到这两个细分的 LCP 分布情况。这应该会填充 Y 以表示“在第 75 个百分位将 LCP 提高 Y 毫秒”:
注意事项
应用上述所有过滤条件后,SXG 的“counterfactual”页面加载应包含以下信息:
- 缓存未命中:如果 Google SXG Cache 没有针对给定网址获取 SXG 的最新副本,则会重定向到您网站上的原始网址。
- 其他搜索结果类型:目前,Google 搜索仅支持使用 SXG 搜索标准网页搜索结果和一些其他类型的搜索结果。而另一些网页(如精选摘要和焦点新闻轮播界面)则会链接到您网站上的原始网址。
- 不符合条件的网址:如果您网站上的某些网页不符合 SXG 使用条件(例如,因为它们不可缓存),它们可能会显示在此集中。
SXG 网页加载和上述非 SXG 网页加载之间可能存在剩余的偏差,但此偏差的幅度应小于“当代研究”部分顶部提到的偏差幅度。例如,不可缓存的网页可能比可缓存的网页更慢或更快。如果您怀疑这可能是个问题,请考虑查看仅限符合 SXG 条件的网址的数据,以了解其结果是否与整体研究相符。
如果您的网站包含部分 AMP 网页,它们可能无法因启用 SXG 而实现性能提升,因为这些网页已从 Google 搜索中预提取。您可以考虑添加过滤条件以排除此类网页,从而进一步“放大”相关更改。
最后,即使解决了所有选择性偏见,也存在幸存者偏见,导致 LCP 改进看起来像是 RUM 统计信息的降级。这篇文章很好地说明了这种风险,并建议通过某种形式的放弃指标来检测是否发生了此类情况。
调查前/后
为了证实这项当代研究的结果,比较一下启用 SXG 前后的 LCP 可能很有用。为了消除上述潜在偏见,请勿将浏览次数限制为 SXG。应查看符合 SXG 条件的结果,即上述细分定义,但没有 isSXG
限制。
请注意,Google 搜索最多可能需要几周时间才能重新抓取您网站上的所有网页,以便确定是否已为这些网页启用 SXG。在这几周���,可能还会出现其他偏差:
- 新浏览器发布或用户硬件改进可能会加快网页加载速度。
- 节假日等重大事件可能会导致流量出现偏离正常情况。
此外,您不妨查看一下使用前后的第 75 个百分位 LCP 前后的整体情况,以便确认上述研究。了解人口的子集并不一定能了解总体人群。例如,假设 SXG 将网页加载的 10% 提高了 800 毫秒。
- 如果这些都已经是网页加载速度最快的 10%,那么第 75 百分位也不会受到影响。
- 如果它们是网页加载速度最慢的 10%,但相较于第 75 百分位的 LCP 开始慢 800 毫秒,则不会影响第 75 百分位。
这些是极端情况的示例,可能并未反映真实情况,但希望能说明问题所在。在实践中,对于大多数网站,SXG 可能会影响第 75 个百分位。跨网站导航往往最慢,预提取往往会有显著改进。
为某些网址选择不自动更改
最后,比较 SXG 性能的一种方法是为您网站上的部分网址停用 SXG。例如,您可以设置 CDN-Cache-Control: no-store
标头来阻止 Cloudflare ASX 生成 SXG。我不推荐这样做。
与其他研究方法相比,这种方法可能存在选择偏差的风险。例如,无论将您网站的首页还是类似的热门网址选入对照组或实验组,都会产生很大的影响。
对照组研究
衡量影响的理想方法是进行受限研究。很遗憾,您目前无法执行此类测试。我们计划将来增加对此类测试的支持。
对照组研究具有以下属性:
- 在实验组中,部分随机的 SXG 网页浏览量会被“保留”,并��为非 SXG 提供。这样,您就可以在对等用户、设备、场景和网页之间进行“对等”比较。
- 分析中会标记这些受限的(也称为反事实)网页浏览。这样就可以查看数据的“放大”视图,在此视图中,我们可以将对照组中的 SXG 网页加载次数与实验中的 SXG 计数器事实进行比较。这反过来又可以减少其他网页加载时产生的干扰,而这些加载不受 SXG 预提取影响。
这会消除上述可能的选择偏见来源,但不会消除 LCP 幸存者偏见的风险。这两个属性都需要启用浏览器或引荐来源网址。
总结
好了!内容太多了。希望本书能更全面地介绍如何在实验室测试中测试 SXG 性能,如何通过实验室测试在紧密反馈环中优化其性能,以及如何在现实世界中衡量其性能。综合以上所有元素,您将能够充分发挥 SXG 的价值,并确保它们能够让您的网站和用户从中受益。
如果您对如何记录 SXG 性能还有其他建议,请告诉我们!提交针对 developer.chrome.com 的错误,并附上您建议的改进。
如需详细了解 Signed Exchange,请参阅 web.dev 文档和 Google 搜索文档。