HTTP 压缩是怎么提升性能的?gzip、br、deflate 有什么区别?
简化版
HTTP 压缩通过减少响应体字节数来降低传输时间,客户端用 Accept-Encoding 声明支持的算法,服务端用 Content-Encoding 返回实际压缩方式。gzip 兼容性最好,Brotli(br)压缩率通常更高,适合 HTTPS 下的文本资源;图片、视频、zip 这类已压缩内容通常不再压缩。压缩能省带宽,但会消耗 CPU,并且要注意缓存、Vary 头和小文件收益。
详细版
典型协商过程:
GET /app.js HTTP/1.1
Accept-Encoding: br, gzip
HTTP/1.1 200 OK
Content-Encoding: br
Vary: Accept-Encoding
压缩最适合 HTML、CSS、JavaScript、JSON、SVG 这类文本内容。一个 500 KB 的 JS 文件,用 gzip 可能变成 150 KB,用 Brotli 可能变成 120 KB;在 10 Mbps 网络下,传输时间从约 400 ms 降到约 96 ms,收益明显。但压缩也要花 CPU,动态响应压缩等级过高可能拖慢服务端。
面试要点包括:客户端和服务端如何协商;为什么要设置 Vary: Accept-Encoding;哪些资源不适合压缩;动态压缩和静态预压缩怎么选;为什么 HTTPS 场景下更常见 Brotli。
完整版教学
一、HTTP 压缩优化的是传输字节数
网络传输时间大致受响应大小和可用吞吐影响。响应越小,下载越快,链路拥塞概率也更低。HTTP 压缩就是在服务端发送前把响应体压缩,客户端收到后解压再使用。对文本资源来说,重复字符串、字段名、空格和代码模式很多,压缩率通常很好。
例如一个 JSON 响应里反复出现 "user_id"、"created_at",gzip 能把这些重复模式编码成更短表示。假设响应从 100 KB 压到 25 KB,在 5 Mbps 有效吞吐下,传输时间约从 160 ms 降到 40 ms。
100 KB * 8 / 5 Mbps ≈ 160 ms
25 KB * 8 / 5 Mbps ≈ 40 ms
压缩不是让网络 RTT 变小,而是让需要传的字节更少。
二、Accept-Encoding 和 Content-Encoding 如何协商
客户端通过 Accept-Encoding 告诉服务端自己支持哪些压缩算法。服务端选择一种支持的算法压缩响应,并通过 Content-Encoding 告诉客户端如何解压。如果服务端不压缩,就不返回该响应头或返回 identity。
Accept-Encoding: br, gzip, deflate
Content-Encoding: gzip
注意 Content-Encoding 描述的是响应体编码,不是资源原始类型。Content-Type: application/json 表示解压后是 JSON;Content-Encoding: gzip 表示传输时被 gzip 压缩。
三、gzip、deflate 和 Brotli 的区别
gzip 是最常见的通用压缩格式,兼容性极好。deflate 历史上存在实现差异,实际 Web 优化里不如 gzip 常用。Brotli 的压缩率通常更高,尤其适合静态文本资源,但压缩开销可能更大,动态高等级压缩要谨慎。
| 算法 | 特点 | 常见使用 |
|---|---|---|
| gzip | 兼容最好,速度和压缩率均衡 | 动态和静态文本 |
| deflate | 历史兼容坑较多 | 较少主动选择 |
| br | 压缩率更高,HTTPS 下主流浏览器支持 | 静态资源、文本 API |
Brotli 常用于 HTTPS,因为主流浏览器通常只在安全上下文里发送 br 支持。工程里常见做法是构建阶段预生成 .br 和 .gz 静态文件,由 Nginx/CDN 直接返回,避免运行时重复压缩。
四、哪些资源不应该压缩
已经压缩过的资源通常不适合再压缩,例如 JPEG、PNG、WebP、MP4、PDF、zip、gzip 文件。再次压缩收益很小,甚至可能变大,还会浪费 CPU。特别小的响应也不一定值得压缩,因为压缩头和 CPU 开销可能超过收益。
| 资源类型 | 是否适合压缩 | 原因 |
|---|---|---|
| HTML/CSS/JS | 适合 | 文本重复多 |
| JSON/SVG | 适合 | 字段和标签重复 |
| JPEG/WebP/MP4 | 不适合 | 已压缩 |
| 小于 1 KB 响应 | 视情况 | 收益有限 |
很多服务器会设置最小压缩大小,例如 gzip_min_length 1024,避免对很小响应做无意义压缩。
五、压缩等级是 CPU 和体积的权衡
压缩等级越高,通常体积越小,但 CPU 消耗越大。动态 API 如果每个请求都用最高等级 Brotli,可能省了几 KB 带宽,却让 CPU 飙高、TTFB 变慢。静态资源可以在构建阶段慢慢压,动态响应要选择中等等级。
假设某 JSON:
| 策略 | 压缩后大小 | 压缩耗时 |
|---|---|---|
| gzip level 1 | 32 KB | 1 ms |
| gzip level 6 | 27 KB | 4 ms |
| br level 11 | 22 KB | 80 ms |
如果是静态 JS 文件,80 ms 构建时花掉没关系;如果是接口实时响应,80 ms 会直接增加用户等待。
六、缓存必须处理 Vary
同一个 URL 对不同客户端可能返回不同编码版本。支持 br 的客户端拿 br,不支持的拿 gzip 或未压缩。如果缓存系统不区分 Accept-Encoding,可能把 br 响应给不支持 br 的客户端,导致乱码或解压失败。
因此服务端应返回:
Vary: Accept-Encoding
这表示缓存键要把 Accept-Encoding 纳入考虑。CDN、反向代理、浏览器缓存都需要据此区分不同压缩版本。面试中提到 Vary,说明你考虑到了真实部署链路。
七、压缩和安全也有关系
压缩会让响应大小和内容相关,在某些场景可能引入侧信道风险。历史上的 BREACH 攻击就利用了“攻击者可控输入 + 响应中有 secret + 响应压缩后长度可观测”的条件推测敏感信息。普通静态资源没有这个问题,但含 CSRF Token、密钥片段的动态页面要谨慎。
风险条件可以这样记:
可控输入 + 同响应里包含 secret + 压缩 + 可观察长度 = 风险组合
解决思路包括避免把用户可控内容和 secret 放在同一压缩上下文、对敏感响应关闭压缩、使用随机填充等。
八、常见误区与追问
- 误区:开启压缩一定提升性能。 文本大响应通常收益明显,但小响应或已压缩资源可能收益很低,还会增加 CPU。
- 误区:Content-Encoding 和 Content-Type 是一回事。 前者表示传输编码,后者表示解压后的媒体类型。
- 误区:Brotli 永远比 gzip 更好。 Brotli 压缩率高,但动态高等级压缩 CPU 成本可能不划算。
- 误区:CDN 缓存不用关心压缩。 必须处理
Vary: Accept-Encoding,否则可能返回客户端不支持的编码。 - 追问:为什么图片视频不建议再压缩? 它们通常已经使用专门算法压缩,再压缩收益小且浪费 CPU。
- 追问:静态资源如何高效使用 Brotli? 构建阶段预压缩生成
.br,由 CDN/Nginx 按客户端能力直接返回。 - 追问:压缩会不会有安全风险? 动态敏感响应可能受长度侧信道影响,需要避免 secret 和可控输入共享压缩上下文。
九、加强记忆
HTTP 压缩按“客户端声明 Accept-Encoding,服务端返回 Content-Encoding,缓存用 Vary 区分版本”来记。gzip 兼容,br 压缩率高,文本资源收益大,图片视频通常别压。动态压缩要权衡 CPU 和 TTFB,静态资源优先预压缩,敏感动态页面还要注意压缩侧信道风险。