如何衡量和优化 Signed Exchange,以最大限度地改进 Signed Exchange
Signed Exchange (SXG) 是一种提高网页速度的方法,主要是 Largest Contentful Paint (LCP)。当引荐网站(目前为 Google 搜索)链接到某个网页时,它们可在用户点击链接之前将该网页预提取到浏览器缓存中。
这样,当网页被预提取时,呈现网页的关键路径上就无需连接网络!当使用 4G 连接时,此页面的加载时间从 2.8 秒缩短到 0.9 秒(其余 0.9 秒主要因 CPU 使用情况而异):
<ph type="x-smartling-placeholder">目前,大多数发布 SXG 的用户都在使用 Cloudflare 简单易用的 Automatic Signed Exchange (ASX) 功能(不过也提供开源方案):
在许多情况下,选中该复选框来启用此功能就足以实现上述的实质性改进。有时,您需要执行一些额外的步骤,以确保这些 SXG 在流水线的每个阶段都能按预期运行,并优化页面以充分利用预提取。
自从 Cloudflare 发布以来的几个月里,我一直在各种 论坛上阅读和回复问题,并学习如何为网站提供建议,以确保他们充分利用其 SXG 部署。这篇帖子是我的一些建议。我将逐步完成以下操作:
- 使用 WebPageTest 来分析 SXG 性能。
- 如果分析步骤显示 SXG 流水线无法正常运行,请调试 SXG 流水线。
- 针对 SXG 预提取优化网页,包括设置最佳
max-age
并预加载会阻止内容呈现的子资源。 - 通过选择合适的实验组和对照组,使用 Google Analytics 衡量 SXG 的效果。
简介
SXG 是一个包含网址、一组 HTTP 响应标头和响应正文(均由 Web PKI 证书以加密方式签名)的文件。当浏览器加载 SXG 时,它会验证以下所有项:
- SXG 尚未过期。
- 签名与网址、标头、正文和证书一致。
- 证书有效且与网址一致。
如果验证失败,浏览器将放弃 SXG,改为提取签名网址。如果验证成功,浏览器就会加载已签名的响应,将其视为直接来自签名网址的响应。这样一来,只要 SXG 自签名后未过期或修改,就可以在任何服务器上重新托管 SXG。
以 Google 搜索为例,SXG 会启用预先抓取搜索结果中的网页功能。对于支持 SXG 的网页,Google 搜索可以预提取托管在 webpkgcache.com 上的网页的缓存副本。这些 webpkgcache.com 网址不会影响网页的显示或行为,因为浏览器会沿用原始的签名网址。预先抓取可以大幅提高网页的加载速度。
分析
要了解 SXG 的优势,请先使用实验室工具分析 SXG 在可重复条件下的性能。您可以使用 WebPageTest 来比较使用和不使用 SXG 预提取的瀑布流和 LCP。
在不使用 SXG 的情况下生成测试,如下所示:
- 转到 WebPageTest 并登录。登录后,系统会保存您的测试记录,以便日后进行比较。
- 输入您要测试的网址。
- 前往高级配置。(您需要为 SXG 测试启用高级配置,因此在此处使用它有助于确保测试选项相同。)
- 在 Test Settings(测试设置)标签页中,将“Connection”(连接)设置为 4G 并增加“Number of Tests to Run”(要运行的测试数量)可能会很有帮助到第 7 步。
- 点击 Start Test。
按照上述步骤使用 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 Validator Chrome 扩展程序访问您的网页,然后点击扩展程序图标以查看缓存网址:
完成这些测试后,前往 Test History(测试历史记录),选择两个测试,然后点击 Compare(比较):
将 &medianMetric=LCP
附加到比较网址,以便 WebPageTest 为比较结果的每一侧选择 LCP 中间值对应的运行作业。(默认值为速度指数的中位数)。
若要比较各个瀑布,请展开瀑布流不透明度部分,然后拖动滑块。要观看视频,请点击调整幻灯影片设置,在该对话框中向下滚动,然后点击观看视频。
如果 SXG 预提取成功,您会看到“with SXG”广告瀑布流不包含 HTML 行,且子资源的提取更早开始。例如,比较“之前”和“之后”此处:
调试
如果 WebPageTest 显示正在预提取 SXG,则表示它已成功完成流水线的所有步骤;您可以跳至优化部分,了解如何进一步改进 LCP。否则,您需要找出流水线中的哪个环节失败以及失败的原因;请继续阅读,了解具体方法。
发布
确保您的网页是作为 SXG 生成的。为此,您需要冒充抓取工具。最简单的方法是使用 SXG Validator Chrome 扩展程序:
<ph type="x-smartling-placeholder">该扩展程序通过 Accept
请求标头(指明它首选 SXG 版本)来提取当前网址。如果您在“Origin”旁边看到对勾标记 (✅),则表示退回了 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,并可能会将其提供给多个访问者。如果您根据不同用户的 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 编入索引,则指向您网页的链接中会包含一个指向 webpkgcache.com 副本的 data-sxg-url
:
如果 Google 搜索认为用户可能会点击该结果,也会预提取该结果:
<link>
元素指示浏览器将 SXG 下载到其预提取缓存中。当用户点击 <a>
元素时,浏览器将使用缓存的 SXG 来呈现网页。
您还可以查看预提取的证据,方法是转到开发者工具中的“Network”标签页,然后搜索包含 webpkgcache
的网址。
如果 <a>
指向 webpkgcache.com,则表示 Signed Exchange 的 Google 搜索索引可正常发挥作用。您可以跳至提取部分。
否则,这可能是因为自您启用 SXG 后 Google 尚未重新抓取您的网页。请尝试使用 Google Search Console 网址检查工具:
如果存在 digest: mi-sha256-03=...
标头,则表示 Google 已成功抓取 SXG 版本。
如果 digest
标头不存在,则可能表明 SXG 未提供给 Googlebot,或者自您启用 SXG 后索引没有更新。
如果某个 SXG 被成功抓取,但仍未链接到它,则可能是因为它不符合 SXG 缓存要求。这些内容将在下一部分进行介绍。
提取
当 Google 搜索将 SXG 编入索引时,会将其副本发送到 Google SXG 缓存,然后 Google SXG 缓存会验证 SXG 是否符合缓存要求。Chrome 扩展程序会显示以下结果:
如果显示对勾标记 (✅),则可以跳至优化。
如果不符合要求,您会看到一个叉号 (❌) 和一条警告消息,说明原因:
在这种情况下,该页面将像启用 SXG 之前一样工作。Google 会在不进行 SXG 预提取的情况下链接到原始主机上的网页。
如果缓存副本已过期且正在后台重新提取,您会看到一个沙漏 (⌛):
SXG 上的 Google 开发者文档也有手动查询缓存的说明。
优化
如果 SXG Validator Chrome 扩展程序显示所有对勾标记 (✅),则表明您有可以向用户提供的 SXG!请阅读下文,了解如何优化网页,以便从 SXG 获得最大的 LCP 改进。
最长年龄
SXG 过期后,Google SXG 缓存将在后台提取新副本。在等待这项提取操作期间,系统会将用户定向到原始主机上未被预提取的网页。您设置 Cache-Control: max-age
的时间越长,此后台提取的发生频率就越低,因此通过预提取降低 LCP 的频率就越高。
这是性能和新鲜度之间的权衡取舍,而缓存允许网站所有者为 SXG 提供介于 2 分钟到 7 天之间的任意一个 maxage 值,以满足每个网页的具体需求。我们发现:
max-age=86400
(1 天)或更长时间是提升效果的最佳选择max-age=120
(2 分钟)不
我们希望在深入研究数据的同时,深入了解这两者之间的价值。
user-agent
有一次,我发现在使用预提取的 SXG 时,LCP 有所增加。我运行了 WebPageTest,比较了不使用 SXG 预提取和使用 SXG 预提取的中位数结果。点击下面的之后:
我发现预提取有效。HTML 即会从关键路径中移除,因此所有子资源都能提前加载。但该绿色虚线的 LCP 从 2 秒增加到了 2.1 秒。
为了诊断这个问题,我查看了胶卷。我发现页面在 SXG 中的呈现方式不同。在纯 HTML 中,Chrome 确定了“最大元素”是标题不过,在 SXG 版本中,该页面添加了一个延迟加载的横幅广告,将标题推送到非首屏,并导致新的最大元素成为延迟加载的 Cookie 意见征求对话框。所有内容的呈现速度都比以前更快,但布局发生变化导致指标报告速度变慢。
我深入研究了一下,发现布局存在差异的原因是页面因 User-Agent
而异,而逻辑中存在错误。尽管 SXG 抓取标头指明了移动版,它却呈现的是桌面版网页。此问题修复后,浏览器再次正确地将网页标题识别为最大的元素。
现在,点击“之后”,我发现预提取的 LCP 下降到 1.3 秒:
SXG 已针对所有外形规格启用。为此,请确保满足以下任一条件:
- 您的网页没有在
User-Agent
之前Vary
(例如,采用自适应设计或单独的移动版/桌面版网址)。 - 如果您的网页使用动态提供内容,它会使用
<meta name=supported-media content=...>
将自身标注为仅限移动设备或桌面设备。
子资源
SXG 可用于预提取子资源(包括图片)以及 HTML。Cloudflare ASX 会扫描同源(第一方)<link rel=preload>
元素的 HTML,并将其转换为与 SXG 兼容的链接标头。如需了解详情,请参阅此处和此处的源代码。
如果一切正常,您会看到来自 Google 搜索的其他预提取:
若要针对 LCP 进行优化,请仔细检查广告瀑布流,并确定哪些资源位于渲染最大元素的关键路径上。如果无法预提取,请考虑是否可以将其从关键路径中移除。请留意在加载完成之前隐藏页面的脚本。
Google SXG Cache 允许最多 20 个子资源预加载,ASX 会确保不超过此限制。不过,添加过多子资源预加载项存在风险。只有在预加载的所有子资源都已完成抓取后,浏览器才会使用预加载的子资源,以避免跨网站跟踪。子资源越多,所有子资源在用户点击到达您的网页之前完成预提取的可能性就越小。
SXG Validator 目前不会检查子资源;要进行调试,请同时使用 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 代码段的上方。这是一种特殊语法,ASX 会在生成 SXG 时将 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 列:
最后,前往“网页指标”报告,选择“选择细分”,然后选择“SXG 反事实”和“SXG”
点击“提交”,您应该会看到两个细分受众群的 LCP 分布情况。这应该会填写“在第 75 个百分位将 LCP 提高 Y 毫秒”对应的 Y:
注意事项
应用上述所有过滤条件后,SXG 反事实页面加载应包含以下内容:
- 缓存未命中:如果 Google SXG 缓存没有指定网址的最新 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 的形式投放。这样可以实现针对等效用户、设备、场景和网页进行比较。
- 这些被截留(又称为“反事实”)网页浏览量在 Google Analytics 中会带有相应标记。这样便可“放大”数据视图,我们可以将对照组中的 SXG 网页加载与实验中的 SXG 反事实进行比较。进而减少其他页面加载中的噪声,不受 SXG 预提取影响。
这可以消除上述可能的选择偏差来源,但并不能消除 LCP 生存偏差的风险。需要浏览器或引荐来源网址才能启用这两项属性。
总结
大功告成!真是很多。希望它能够更全面地说明如何在实验室测试中测试 SXG 性能,如何通过实验室测试在紧密反馈环中优化其性能,以及最后如何衡量其在现实环境中的性能。综合所有这些因素有助于您充分利用 SXG,并确保它们让您的网站和用户受益。
如果您对如何记录 SXG 的表现还有其他建议,请告诉我们!提交针对 developer.chrome.com 的错误,并提供您建议的改进措施。
如需详细了解 Signed Exchange,请参阅 web.dev 文档和 Google 搜索文档。