
开启 Gzip 动态压缩
这是最常见的配置(gzip on;)。Nginx 会在发送文件时,实时把内容压缩,然后再发给浏览器。
- 关键特征 :
Content-Encoding: gzip:这是最明显的标志。它告诉浏览器:"我发给你的数据是压缩过的,你收到后记得用 Gzip 解压再显示。"Content-Length消失或变小 :- 在 HTTP/1.1 的 Chunked 模式下,这个头可能会消失(因为长度是动态计算的)。
- 如果存在,这个数值会远小于源文件的大小(例如原文件 100KB,压缩后可能只有 30KB)。
Vary: Accept-Encoding:通常会出现这个头。它的意思是"缓存服务器请注意,如果用户请求头里带了Accept-Encoding: gzip,你就给他看压缩版;如果没带,就给他看原版"。这是为了兼容不支持压缩的老浏览器。
开启静态压缩
这是高性能配置(gzip_static on;)。意思是你在部署项目时,已经提前把 app.js 压缩成了 app.js.gz 放在服务器上了。Nginx 发现浏览器支持 Gzip,就直接把那个 .gz 文件发过去,不再实时压缩。
- 关键特征 :
Content-Encoding: gzip:和动态压缩一样,依然有这个头,告诉浏览器要解压。Content-Length:这里的长度是那个.gz文件的大小,而不是源文件的大小。- 性能差异:从 HTTP 头里看不出来是"静态"还是"动态"压缩,但静态压缩不占用 Nginx 的 CPU 去实时计算,响应速度更快。
不开启压缩
这是最原始的状态,Nginx 直接把文件原封不动地读出来发给浏览器。
- 关键特征 :
- 没有
Content-Encoding:因为没有压缩,所以不需要告诉浏览器解压。 Content-Length很大 :这里的数值是文件的 原始大小。比如一个 1MB 的 JS 文件,这里就是 1048576 字节。- 传输慢:因为数据量大,网络传输时间会变长。
- 没有
总结对比表
| 特性 | 不开启压缩 | 开启 Gzip (动态) | 开启 Gzip (静态) |
|---|---|---|---|
| Content-Encoding | (无) | gzip |
gzip |
| Content-Length | 大 (源文件大小) | 小 (压缩后大小) | 小 (压缩后大小) |
| CPU 消耗 | 低 | 高 (实时压缩消耗 CPU) | 低 (直接读文件) |
| 传输速度 | 慢 | 快 | 快 |
| Vary 头 | 通常无 | 通常有 Accept-Encoding |
通常有 Accept-Encoding |
Chunked 模式下,客户端如何知道什么时候传完了
这正是引入 Chunked 模式的原因------服务器在开始发送数据时,根本不知道总大小是多少。
既然 HTTP 头里没有 Content-Length,客户端(浏览器)就无法通过计算字节数来判断结束。因此,HTTP/1.1 协议规定了一种特殊的**"结束标记"**机制。
简单来说,服务器发送数据的格式就像这样:
[数据块大小] -> [数据内容] -> [数据块大小] -> [数据内容] -> ... -> [0] -> [结束]
具体流程如下:
🔢 分块发送
服务器把数据切成一块一块的。每一块数据的前面,都会加上一个十六进制的数字,表示这一块有多大。
举个例子:
假设服务器要发送 "Hello World" 这句话,它可能会分成两块发:
- 第一块:
- 服务器先发:
5\r\n(意思是:接下来这块有 5 个字节) - 服务器再发:
Hello
- 服务器先发:
- 第二块:
- 服务器先发:
6\r\n(意思是:接下来这块有 6 个字节) - 服务器再发:
World(注意前面有个空格,刚好 6 字节)
- 服务器先发:
🏁 发送结束标记
当所有数据都发完后,服务器会发送一个大小为 0 的块。
- 服务器发:
0\r\n - 浏览器一读到
0,立刻就明白了:"哦,数据发完了,没有下一块了。"
📦 尾部(可选)
在 0 之后,服务器还可以再发一些额外的 HTTP 头(比如数字签名),最后以一个空行结束整个响应。
📌 总结
在 Chunked 模式下,客户端判断传输结束的依据不是"读了多少个字节",而是读到了 0\r\n 这个结束标记。
这也就是为什么你在 Nginx 开启 Gzip 压缩时,经常看不到 Content-Length 的原因:
因为 Nginx 是边压缩边发送的,它在还没开始发的时候,根本不知道压缩后的最终体积是多少,所以只能放弃 Content-Length,改用 Transfer-Encoding: chunked 这种"发一点报一点"的方式。