通常,缓存可以通过存储数据来提高性能,因此可以更快地处理对同一数据的未来请求。例如,来自网络的缓存资源可以避免到服务器的往返。缓存的计算结果可以省略执行相同计算的时间。
在 Chrome 中,缓存机制的使用方法有很多种,HTTP 缓存便是其中一种。
Chrome 的 HTTP 缓存目前的运作方式
从版本 85 开始,Chrome 会使用从网络获取的资源各自的资源网址作为缓存键,缓存这些资源。(缓存键用于标识缓存的资源。)
以下示例说明了在三种不同的环境中如何缓存和处理单张图片:
![缓存键:https://x.example/doge.png](https://developer.chrome.google.cn/static/blog/http-cache-partitioning/image/cache-key-httpsxexam-d673e1bd1a43f.png?authuser=4&hl=hu)
https://x.example/doge.png
}
用户访问了请求图像 (https://x.example/doge.png
) 的页面 (https://a.example
)。系统从网络请求图像,并使用 https://x.example/doge.png
作为键进行缓存。
![缓存键:https://x.example/doge.png](https://developer.chrome.google.cn/static/blog/http-cache-partitioning/image/cache-key-httpsxexam-f8c1f9f7d95ce.png?authuser=4&hl=hu)
https://x.example/doge.png
}
同一用户访问了另一个页面 (https://b.example
),而该页面请求了同一图片 (https://x.example/doge.png
)。浏览器使用图片网址作为键来检查其 HTTP 缓存,确认其是否已缓存此资源。浏览器在其缓存中找到匹配项,因此会使用该资源的缓存版本。
![缓存键:https://x.example/doge.png](https://developer.chrome.google.cn/static/blog/http-cache-partitioning/image/cache-key-httpsxexam-62cac78cd2789.png?authuser=4&hl=hu)
https://x.example/doge.png
}
图片是否在 iframe 中加载无关紧要。如果用户使用 iframe (https://d.example
) 访问其他网站 (https://c.example
),并且 iframe 请求了同一图片 (https://x.example/doge.png
),则浏览器仍然可以从缓存中加载图片,因为所有网页的缓存键都相同。
从性能的角度来看,这种机制已经运作了很长时间。但是,网站响应 HTTP 请求所需的时间可以表明浏览器过去访问过同一资源,这会使浏览器遭受安全和隐私攻击,例如:
- 检测用户是否访问过特定网站:攻击者可以检测用户的浏览记录,方法是检查缓存中是否具有可能特定于特定网站或网站同类群组的资源。
- 跨网站搜索攻击:攻击者可以通过检查特定网站使用的“无搜索结果”图片是否在浏览器的缓存中来检测用户搜索结果中是否包含任意字符串。
- 跨网站跟踪:缓存可用于将类似 Cookie 的标识符存储为跨网站跟踪机制。
为降低这些风险,从 Chrome 86 开始,Chrome 将对其 HTTP 缓存进行分区。
缓存分区对 Chrome 的 HTTP 缓存有何影响?
借助缓存分区,系统将使用新的“网络隔离键”以及资源网址对缓存的资源进行键控。网络隔离键由顶级网站和当前框架网站组成。
我们再来看一下上一个示例,了解缓存分区在不同上下文中的工作原理:
![缓存键 { https://a.example, https://a.example, https://x.example/doge.png}](https://developer.chrome.google.cn/static/blog/http-cache-partitioning/image/cache-key-httpsexam-97fe4405170ee.png?authuser=4&hl=hu)
https://a.example
, https://a.example
, https://x.example/doge.png
}
用户访问了请求图片 (https://x.example/doge.png
) 的网页 (https://a.example
)。在这种情况下,系统会从网络请求图片,并使用由 https://a.example
(顶级网站)、https://a.example
(当前框架网站)和 https://x.example/doge.png
(资源网址)作为键的元组进行缓存。(请注意,当资源请求来自顶级框架时,顶级网站和网络隔离键中的当前框架网站是相同的)。
![缓存键 { https://a.example, https://a.example, https://x.example/doge.png}](https://developer.chrome.google.cn/static/blog/http-cache-partitioning/image/cache-key-httpsexam-d34238052404.png?authuser=4&hl=hu)
https://b.example
, https://b.example
, https://x.example/doge.png
}
同一用户访问不同的页面 (https://b.example
),而该页面请求了相同的图像 (https://x.example/doge.png
)。虽然在上一个示例中加载了相同的图像,但由于键不匹配,因此不会成为缓存命中。
系统会从网络请求图片,并使用由 https://b.example
、https://b.example
和 https://x.example/doge.png
组成的元组作为键进行缓存。
![缓存键 { https://a.example, https://a.example, https://x.example/doge.png}](https://developer.chrome.google.cn/static/blog/http-cache-partitioning/image/cache-key-httpsexam-b2b072114f1fc.png?authuser=4&hl=hu)
https://a.example
, https://a.example
, https://x.example/doge.png
}
现在,用户会返回 https://a.example
,但这一次图片 (https://x.example/doge.png
) 嵌入到了 iframe 中。在这种情况下,键是包含 https://a.example
、https://a.example
和 https://x.example/doge.png
的元组,并且会发生缓存命中。(请注意,当顶级网站和 iframe 是同一网站时,可以使用通过顶级框架缓存的资源。
![缓存键 { https://a.example, https://a.example, https://x.example/doge.png}](https://developer.chrome.google.cn/static/blog/http-cache-partitioning/image/cache-key-httpsexam-7f72d1a4803af.png?authuser=4&hl=hu)
https://a.example
, https://c.example
, https://x.example/doge.png
}
用户回到 https://a.example
,但这次图片托管在 https://c.example
的 iframe 中。
在这种情况下,系统会从网络下载图片,因为缓存中没有与由 https://a.example
、https://c.example
和 https://x.example/doge.png
组成的键匹配的资源。
![缓存键 { https://a.example, https://a.example, https://x.example/doge.png}](https://developer.chrome.google.cn/static/blog/http-cache-partitioning/image/cache-key-httpsexam-bfba3d8b780d.png?authuser=4&hl=hu)
https://a.example
, https://c.example
, https://x.example/doge.png
}
如果域名包含子域名或端口号,该怎么办?用户访问 https://subdomain.a.example
,后者嵌入了请求图片的 iframe (https://c.example:8080
)。
由于密钥是根据“scheme://eTLD+1”创建的,因此子网域和端口号会被忽略。因此,会发生缓存命中。
![缓存键 { https://a.example, https://a.example, https://x.example/doge.png}](https://developer.chrome.google.cn/static/blog/http-cache-partitioning/image/cache-key-httpsexam-752e8576ec61f.png?authuser=4&hl=hu)
https://a.example
, https://c.example
, https://x.example/doge.png
}
如果 iframe 嵌套了多次,该怎么办?用户访问 https://a.example
,后者嵌入了一个 iframe (https://b.example
),而 iframe (https://b.example
) 又嵌入了另一个 iframe (https://c.example
),而该 iframe 最终会请求图片。
由于键取自顶层帧 (https://a.example
) 和加载资源的即时帧 (https://c.example
),因此会发生缓存命中。
常见问题解答
我的 Chrome 上是否已启用该功能?该如何确认?
此功能逐步推广到 2020 年底。如需检查您的 Chrome 实例是否已支持该功能,请执行以下操作:
- 打开
chrome://net-export/
,然后按开始记录到磁盘。 - 指定日志文件在计算机上的保存位置。
- 花一分钟时间在 Chrome 上浏览网页。
- 返回
chrome://net-export/
,然后按 Stop Logging。 - 前往
https://netlog-viewer.appspot.com/#import
。 - 按选择文件并传递您保存的日志文件。
您将看到日志文件的输出。
在同一页面上,找到 SplitCacheByNetworkIsolationKey
。如果后跟 Experiment_[****]
,则表示您的 Chrome 已启用 HTTP 缓存分区。如果后跟 Control_[****]
或 Default_[****]
,则表示未启用。
如何在 Chrome 上测试 HTTP 缓存分区?
如需在 Chrome 上测试 HTTP 缓存分区,您需要使用以下命令行标志启动 Chrome:--enable-features=SplitCacheByNetworkIsolationKey
。请按照运行带有标志的 Chromium 中的说明操作,了解如何在您的平台上使用命令行标志启动 Chrome。
作为 Web 开发者,我该采取什么措施来应对这项变更?
这不是一项破坏性更改,但可能会造成某些 Web 服务的性能方面的考虑因素。
例如,如果网站在多个网站上提供大量高度可缓存的资源(例如字体和热门脚本),其流量可能会增加。此外,使用此类服务的用户可能更加依赖这些服务。
(人们提议以可保护隐私的方式启用共享库,称为 Web 共享库(演示视频),但相关建议仍在考虑中。)
这种行为变化会产生什么影响?
整体缓存未命中率提高了约 3.6%,FCP (First Contentful Paint) 的变化幅度适中 (~0.3%),并且从网络加载的总字节数占比增加了约 4%。如需详细了解对性能的影响,请参阅 HTTP 缓存分区说明文档。
这项指标是标准化的吗?其他浏览器的行为方式是否有所不同?
尽管浏览器的行为方式有所不同,但“HTTP 缓存分区”已在提取规范中进行了标准化:
- Chrome:使用顶级 scheme://eTLD+1 和框架 scheme://eTLD+1
- Safari:使用顶级 eTLD+1
- Firefox:计划使用顶级 scheme://eTLD+1 来实现,并考虑添加 Chrome 等第二个密钥
如何处理从工作器提取的内容?
专用工作器使用与其当前帧相同的密钥。Service Worker 和共享 Worker 更为复杂,因为它们可能在多个顶级网站之间共享。适合他们的解决方案目前正在讨论中。