2、HTTP协议中的压缩:Accept-Encoding与Content-Encoding、gzip与deflate、Brotli算法
聊到接口资源压缩,HTTP 协议这一层是绕不开的。说白了,它就是浏览器和服务器之间「握手」商量压缩方式的那个过程。我刚开始接触这块时,总觉得不就是加个头嘛,后来线上出过一次问题才明白——这里面的门道,比想象中深。
2.1 协商机制:Accept-Encoding 与 Content-Encoding
HTTP 压缩的核心,是一对请求/响应头。客户端说「我能解哪些格式」,服务器说「我用了哪种格式」。就这么简单。
- Accept-Encoding:客户端发起的请求头,告诉服务器自己支持的压缩算法。比如
Accept-Encoding: gzip, deflate, br。 - Content-Encoding:服务器返回的响应头,告诉客户端实际用了哪种算法。比如
Content-Encoding: br。
你想想看,如果服务器返回了 Brotli 压缩的内容,但客户端根本不支持,那就会乱码。所以这个协商过程是必须的。我在项目中遇到过一种情况:某个老旧的内网客户端只支持 gzip,但 CDN 默认返回了 Brotli,结果页面白屏了半小时。嗯,从那以后我养成了一个习惯——永远在降级策略里保留 gzip。
核心原则:服务器必须根据 Accept-Encoding 选择客户端支持的算法,不能自作主张。
2.2 gzip 与 deflate:老牌选手的恩怨
gzip 和 deflate,这两个名字你肯定不陌生。但很多人以为 deflate 就是 gzip 的别称,其实不是。
2.2.1 gzip
gzip 是目前最通用的 HTTP 压缩格式。它基于 DEFLATE 算法,但外面包了一层文件头和校验和。几乎所有浏览器和服务器都支持。我个人习惯在生产环境把 gzip 作为保底选项——兼容性最好,没有之一。
2.2.2 deflate
deflate 这个名字其实是个历史误会。HTTP 协议里的 deflate,本意是指 zlib 格式(zlib 头 + DEFLATE 数据流),但有些实现把它理解成了 原始 DEFLATE 数据流。这就导致了兼容性问题。我记得有一次排查接口响应乱码,查了半天发现是 Nginx 和客户端对 deflate 的理解不一致。从那以后,我建议团队直接放弃 deflate,统一用 gzip 或 Brotli。
| 特性 | gzip | deflate |
|---|---|---|
| 底层算法 | DEFLATE | DEFLATE(zlib 或原始) |
| 文件头 | 10 字节 gzip 头 | 2 字节 zlib 头(或没有) |
| 校验和 | 8 字节 CRC32 | 4 字节 Adler-32 |
| 浏览器兼容性 | 几乎 100% | 存在歧义,部分浏览器不支持 |
| 压缩率 | 中等 | 略低于 gzip(头开销小) |
避坑指南:我曾经在生产环境遇到过 deflate 导致部分 Android 浏览器无法解压的问题。如果你不是必须兼容极端老旧系统,不要用 deflate。gzip 更安全。
2.3 Brotli 算法:新一代压缩之王
Brotli 是 Google 在 2015 年推出的压缩算法。它的目标很明确——比 gzip 压缩率更高,解压速度更快。我最早在 Google 的 PageSpeed 模块里接触到它,当时就觉得这玩意儿有前途。
2.3.1 为什么 Brotli 更强?
- 更大的字典:Brotli 内置了一个 120KB 的静态字典,包含常见的 HTML、CSS、JS 片段。gzip 没有这个。
- 更智能的上下文建模:它能根据上下文预测下一个符号,减少冗余。
- 多级压缩:从 0(最快)到 11(最慢但最小),你可以根据场景选择。
你想想看,同样一份 100KB 的 JavaScript 文件,gzip 能压到 30KB,Brotli 能压到 22KB。对于移动端用户来说,这 8KB 的差距可能就是 200ms 的加载时间。
2.3.2 实际使用建议
我个人习惯在 CDN 和反向代理层同时开启 gzip 和 Brotli。客户端优先选择 Brotli,不支持则降级到 gzip。Nginx 的配置大概是这样:
# 启用 Brotli 压缩
brotli on;
brotli_comp_level 6;
brotli_types text/html text/css application/javascript image/svg+xml;
# 保留 gzip 作为降级
gzip on;
gzip_comp_level 5;
gzip_types text/html text/css application/javascript;
小技巧:Brotli 的压缩级别 4-6 是性价比最高的区间。级别 11 虽然压缩率最高,但压缩时间可能增加 10 倍,适合静态资源预压缩。动态响应用级别 4 就够了。
2.4 三种算法对比一览
| 算法 | 压缩率 | 压缩速度 | 解压速度 | 浏览器支持 | 推荐场景 |
|---|---|---|---|---|---|
| gzip | 中等 | 快 | 快 | 所有浏览器 | 保底方案、动态响应 |
| deflate | 略高于 gzip | 快 | 快 | 部分浏览器有歧义 | 不推荐 |
| Brotli | 高(比 gzip 高 15-25%) | 慢(级别越高越慢) | 快 | 现代浏览器(Chrome、Firefox、Safari 11+) | 静态资源、HTTPS 场景 |
2.5 知识体系:HTTP 压缩协商流程
下面这张图,是我自己画的一个流程。它把客户端、服务器、CDN 之间的协商过程串起来了。你看一遍,应该就能记住。
2.6 实战中的几个坑
最后,分享几个我踩过的坑。希望能帮你省点时间。
- 不要对图片用 gzip/Brotli:JPEG、PNG 已经是压缩格式,再压一遍不仅没效果,还浪费 CPU。我见过有人对图片也开 gzip,结果压缩率不到 1%。
- 注意 HTTPS 与 Brotli 的关系:Brotli 在 HTTP/2 下表现最好,但 HTTP/1.1 也支持。不过有些老版代理会剥离 Brotli 头,导致降级。我曾经排查过一个 CDN 问题,发现是中间代理把
Accept-Encoding: br给删了。 - 预压缩静态资源:对于不常变的 JS/CSS,我建议在构建阶段就生成 .gz 和 .br 文件。Nginx 可以直接用
gzip_static on和brotli_static on来 serve,省去实时压缩的开销。
一句话总结:Brotli 是首选,gzip 是保底,deflate 忘掉它。HTTP 压缩协商就是客户端说「我能吃啥」,服务器说「给你这个」——别搞复杂了。