概述
HTTP 中使用压缩算法来减小通过网络下载的资源的大小,从而降低带宽成本和页面加载时间。无损 HTTP 压缩算法通过查找源中的冗余来工作:例如,像字符串 "function"
这样的文本重复出现的地方。然后,它们只包含一份冗余字符串的副本,并用对该副本的引用替换资源中出现的所有冗余字符串。由于引用比字符串短,因此压缩版本更短。
注意:这项技术之前的一次尝试名为 SDCH (Shared Dictionary Compression for HTTP),但从未得到广泛支持,并于 2017 年被移除。压缩字典传输是规范更完善、实现更健壮,并具有更广泛行业共识的实现。
例如,考虑以下 JavaScript
function a() {
console.log("Hello World!");
}
function b() {
console.log("I am here");
}
这可以通过将重复的字符串替换为对先前位置和字符数的引用来压缩,如下所示
function a() { console.log("Hello World!"); } [0:9]b[10:20]I am here[42:46]
在此示例中,[0:9]
指的是复制从字符 0 开始的 9 个字符。请注意,这是一个简化示例,旨在演示概念,实际算法比这更复杂。
客户端下载后可以反转压缩以重新创建原始的未压缩资源。
压缩字典
像 Brotli 压缩 和 Zstandard 压缩 这样的算法通过允许使用常用字符串字典来提高效率,因此您无需在压缩资源中包含它们的任何副本。这些算法附带一个预定义的默认字典,用于压缩 HTTP 响应时。
压缩字典传输在此基础上,允许您提供自己的字典,这特别适用于特定的资源集。压缩算法在压缩和解压缩资源时可以将其作为字节源进行引用。
假设上一个示例中的引用包含在该公共字典中,这可以进一步减少为
[d0:9]a[d10:20]Hello World![d42:46] [d0:9]b[d10:20]I am here[d42:46]
字典可以是一个独立的资源,仅用于压缩字典传输,也可以是网站本来就需要的一个资源。
例如,假设您的网站使用 JavaScript 库。您通常会加载特定版本的库,并且可能会在库名称中包含版本名称,例如 <script src="my-library.v1.js">
。当浏览器加载您的页面时,它将作为子资源获取库的副本。
如果您随后更新到库的 v2 版本,库的大部分代码可能保持不变。因此,网站可以通过告诉浏览器使用 my-library.v1.js
作为 my-library.v2.js
的压缩字典,从而大大减小 my-library.v2.js
的下载大小。这样,v1 和 v2 之间所有相同的字符串都不需要包含在 v2 的下载中,因为浏览器已经拥有它们。my-library.v2.js
的大部分下载大小就是两个版本之间的差异。
压缩字典传输可以比使用默认内置字典的压缩方案获得数量级更高的压缩比:有关一些实际结果,请参阅 压缩字典传输示例。
字典格式
压缩字典不遵循任何特定格式,也没有特定的 MIME 类型。它们是常规文件,可用于压缩具有相似内容的其他文件。
文件的先前版本通常包含大量相似内容,这就是它们成为优秀字典的原因。使用文件的先前版本作为字典允许压缩算法有效地引用所有未更改的内容,并仅捕获新版本中相对较小的差异。这种方法被称为增量压缩。
另一种方法是将常用字符串(例如您的 HTML 模板)一起列在一个新的 dictionary.txt
文件中,以便它可以用于压缩网站上的 HTML 页面。您可以通过使用专门的工具(例如 Brotli 的字典生成器)进一步优化此过程,该工具可将字典缩小到最小大小,同时将重叠最小化。
字典也可以有效地压缩二进制格式。例如,WASM 二进制文件是大型资源,也可以从增量压缩中受益。
现有资源作为字典
要将资源用作字典,服务器应在提供资源的响应中包含 Use-As-Dictionary
标头
Use-As-Dictionary: match="/js/app.*.js"
此标头的值指示哪些资源可以使用此资源作为字典:在此示例中,这包括所有 URL 与给定 模式 匹配的资源。
当稍后请求与给定模式匹配的资源(例如 app.v2.js
)时,请求将在 Available-Dictionary
标头中包含可用字典的 SHA-256 哈希值,以及 Accept-Encoding
标头中的 dcb
和/或 dcz
值(用于分别使用 Brotli 或 ZStandard 进行增量压缩)
Accept-Encoding: gzip, br, zstd, dcb, dcz
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:
然后,服务器可以响应一个经过适当编码的响应,其中所选的内容编码在 Content-Encoding
标头中给出
Content-Encoding: dcb
如果响应是可缓存的,则必须包含 Vary
标头,以防止缓存将字典压缩资源提供给不支持它们的客户端,或提供使用错误字典压缩的响应
Vary: accept-encoding, available-dictionary
还可以在 Use-As-Dictionary
标头中提供一个可选的 id
,以便服务器在不按哈希存储字典的情况下更容易找到字典文件
Use-As-Dictionary: match="/js/app.*.js", id="dictionary-12345"
如果提供了此项,则该值将在未来的请求中通过 Dictionary-ID
标头发送
Accept-Encoding: gzip, br, zstd, dcb, dcz
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:
Dictionary-ID: "dictionary-12345"
服务器仍必须检查 Available-Dictionary
标头中的哈希值 — Dictionary-ID
是服务器识别字典的附加信息,但不能替代 Available-Dictionary
标头的必要性。
独立字典
HTML 文档还可以向浏览器提供一个压缩字典,该字典不是浏览器通过 <script>
标签等元素下载的资源。有两种方法可以实现这一点
-
包含一个
<link>
元素,其rel
属性设置为compression-dictionary
html<link rel="compression-dictionary" href="/dictionary.dat" />
-
使用
Link
标头引用字典httpLink: </dictionary.dat>; rel="compression-dictionary"
然后,浏览器将在空闲时间下载此字典,并且该响应必须包含 Use-As-Dictionary
标头
Use-As-Dictionary: match="/js/app.*.js"
从这里开始,当请求匹配的资源时,过程与前面的示例类似。
创建字典压缩响应
字典压缩响应可以使用 Brotli 或 ZStandard 算法,并有两个额外要求:它们还必须包含一个魔术标头和嵌入式字典哈希。
字典压缩资源可以动态创建,但对于静态资源,最好在构建时预先创建。当使用先前版本作为字典时,这将需要决定创建多少个增量压缩版本——只为最新版本,或为最后 X 个版本(X 为某个值)。
给定一个名为 dictionary.text
的字典文件和一个名为 data.text
的待压缩文件,以下 Bash 命令将使用 Brotli 压缩该文件,生成一个名为 data.txt.dcb
的压缩文件
echo -en '\xffDCB' > data.txt.dcb && \
openssl dgst -sha256 -binary dictionary.txt >> data.txt.dcb && \
brotli --stdout -D dictionary.txt data.txt >> data.txt.dcb
给定相同的输入文件,以下 Bash 命令将使用 ZStandard 压缩该文件,生成一个名为 data.txt.dcz
的压缩文件
echo -en '\x5e\x2a\x4d\x18\x20\x00\x00\x00' > data.txt.dcz && \
openssl dgst -sha256 -binary dictionary.txt >> data.txt.dcz && \
zstd -D dictionary.txt -f -o tmp.zstd data.txt && \
cat tmp.zstd >> data.txt.dcz
请注意,您需要在本地安装 OpenSSL 以及 Brotli 或 ZStandard。
限制
压缩算法存在安全攻击风险,因此压缩字典传输存在多项限制,其中包括
- 字典必须与使用字典的资源同源。
- 字典压缩资源必须与文档源同源,或遵循 CORS 规则,因此需要使用
crossorigin
属性请求,并使用适当的Access-Control-Allow-Origin
标头提供服务。 - 字典受常见的 HTTP 缓存分区限制,因此即使它们下载相同的资源,也不能在不同源之间共享。每个源都需要重新下载字典。
此外,字典本身可能成为跟踪媒介,因此当禁用 Cookie 或启用其他额外的隐私保护时,浏览器可能会限制此功能。
规范
规范 |
---|
未知规范 |
浏览器兼容性
html.elements.link.rel.compression-dictionary
加载中…
http.headers.Accept-Encoding.dcb
加载中…
http.headers.Accept-Encoding.dcz
加载中…
http.headers.Available-Dictionary
加载中…
http.headers.Content-Encoding.dcb
加载中…
http.headers.Content-Encoding.dcz
加载中…
http.headers.Dictionary-ID
加载中…
http.headers.Use-As-Dictionary
加载中…