HTTP 中的压缩

压缩是提高网站性能的重要方法。对于某些文档,大小减少高达 70% 可以降低带宽容量需求。多年来,算法也变得更高效,并且客户端和服务器都支持新的算法。

在实践中,Web 开发者不需要实现压缩机制,浏览器和服务器都已实现,但他们必须确保服务器配置得当。压缩发生在三个不同的层面:

  • 首先,一些文件格式使用特定的优化方法进行压缩;
  • 然后,可以在 HTTP 级别进行通用压缩(资源从头到尾进行压缩传输);
  • 最后,可以在连接级别,即 HTTP 连接的两个节点之间定义压缩。

文件格式压缩

每种数据类型都存在一些冗余,即浪费的空间。如果文本通常可以有高达 60% 的冗余,那么对于音频和视频等其他媒体类型,这个比例可能会更高。与文本不同,这些其他媒体类型使用大量空间来存储数据,因此优化存储和回收空间的需求很早就显现出来。工程师们设计了用于专门为此目的而设计的文件格式的优化压缩算法。用于文件的压缩算法可以分为两大类:

  • 无损压缩,其中压缩-解压缩循环不会改变恢复的数据。它与原始数据(字节到字节)匹配。对于图像,gifpng 使用无损压缩。
  • 有损压缩,其中循环以(希望)用户无法察觉的方式改变原始数据。Web 上的视频格式是有损的;jpeg 图像格式也是有损的。

某些格式可以用于无损或有损压缩,例如 webp,并且通常有损算法可以配置为或多或少地压缩,这当然会导致或多或少的质量。为了获得更好的网站性能,理想的做法是尽可能多地压缩,同时保持可接受的质量水平。对于图像,工具生成的图像可能未针对 Web 充分优化;建议使用能够以所需质量尽可能多地压缩的工具。有许多专门的工具

有损压缩算法通常比无损压缩算法更高效。

注意:由于压缩对特定类型的文件效果更好,因此通常不需要再次压缩它们。事实上,这往往适得其反,因为开销成本(算法通常需要一个会增加初始大小的字典)可能高于压缩的额外增益,从而导致文件更大。请勿对已压缩格式的文件使用以下两种技术。

端到端压缩

对于压缩,端到端压缩是网站性能最大提升所在。端到端压缩是指服务器对消息正文进行的压缩,并且将保持不变直到到达客户端。无论中间节点是什么,它们都不会触及正文。

A server sending a compressed HTTP body to a client via network nodes. The body is not decompressed at any hop through the network until it reaches the client.

所有现代浏览器和服务器都支持它,唯一需要协商的是要使用的压缩算法。这些算法针对文本进行了优化。在 1990 年代,压缩技术快速发展,并且在可选算法集中添加了许多连续的算法。如今,只有两种相关:gzip,最常见的一种,以及新挑战者 br

为了选择要使用的算法,浏览器和服务器使用主动内容协商。浏览器发送一个带有它支持的算法及其优先级顺序的Accept-Encoding头,服务器选择一个,用它来压缩响应的正文,并使用Content-Encoding头告诉浏览器它选择的算法。由于内容协商已用于根据其编码选择表示形式,服务器必须在响应中发送一个至少包含Accept-EncodingVary头;这样,缓存将能够缓存资源的不同表示形式。

A client requesting content with an 'Accept-Encoding: br, gzip' header. The server responds with a body compressed using the Brotli algorithm and the required 'Content-Encoding' and 'Vary' headers.

由于压缩带来显著的性能提升,建议对所有文件启用压缩,除了已经压缩的文件,如图像、音频文件和视频。

Apache 支持压缩并使用mod_deflate;对于 Nginx,有ngx_http_gzip_module;对于 IIS,有<httpCompression>元素。

压缩字典传输

现代压缩格式,如Brotli 压缩Zstandard 压缩,可以使用常用数据字典来进一步提高压缩率,而不仅仅是从正在压缩的文件内部引用这些数据。通常,对于 HTTP 响应,这会使用该格式中包含的预定义静态字典(例如,Brotli 静态字典可在源代码中找到)。

压缩字典传输使开发者能够指定一个资源,该资源可以用作未来请求的字典。这既可以是特定的字典文件,也可以是现有资源(例如,在下载 app.v2.js 时使用 app.v1.js 作为字典)。这通常会改善压缩效果,从而缩短加载时间。在 app.vX.js 示例中,大部分下载将仅包含两个版本之间的增量,并且通用字节可以从已下载的原始 app.v1.js 文件中引用。

逐跳压缩

逐跳压缩,尽管与端到端压缩相似,但有一个根本区别:压缩不是在服务器上的资源上发生,从而创建并传输特定表示形式,而是在客户端和服务器之间路径上任意两个节点之间的消息体上发生。连续中间节点之间的连接可以应用不同的压缩。

A server sending an uncompressed HTTP body to a client via network nodes. The body is compressed and decompressed by nodes on the network depending on 'Transfer-Encoding' headers before it reaches the client.

为此,HTTP 使用类似于端到端压缩的内容协商机制:发送请求的节点通过 TE 标头来表明其意图,另一个节点选择适当的方法,应用它,并使用 Transfer-Encoding 标头来指示其选择。

A client requesting content from a server with no compression-related headers. The server responds with an uncompressed body. The body is compressed and decompressed by nodes on the network before it reaches the client.

实际上,逐跳压缩对于服务器和客户端是透明的,并且很少使用。TETransfer-Encoding 主要用于分块发送响应,允许在不知道资源长度的情况下开始传输资源。

请注意,在跳级别使用 Transfer-Encoding 和压缩非常罕见,以至于大多数服务器(如 Apache、Nginx 或 IIS)都没有简单的方法来配置它。此类配置通常发生在代理级别。

另见