Accept-Encoding 告诉服务端「我支持哪些压缩方式」。
它也带 q 值,也遵守一条铁律:客户端不提,服务端不许自己压。
上一节那个字段死了,这一个活得很好——它是每个真实响应里几乎都会出现的一对字段之一。
一、几个常见取值
| 值 | 说明 | 现状 |
|---|---|---|
gzip |
事实上的标准压缩 | 几乎都支持 |
deflate |
zlib 格式,历史上实现混乱 | 支持但少用 |
br |
Brotli,比 gzip 再小一截 | 现代浏览器都支持 |
identity |
不压缩(默认隐含) | — |
* |
上面没列的都可以 | 少见 |
deflate 的历史问题值得记一句:规范和早期实现不一致,有的发 zlib 包装过的数据, |
||
| 有的发裸 deflate 流,客户端经常解不出来。所以现实中优先用 gzip 或 br。 |
二、q 值在这里怎么生效
Demo 里有一组 gzip;q=0.3, deflate;q=0.9,服务端选了 deflate。因为客户端明说更想要 deflate。
这跟很多人的直觉不同——代码里常常是先看 gzip 有没有,有就用。
正确做法是按 q 排序再挑,跟 Accept 的规则一样。
另一组 gzip;q=0 更微妙:q=0 不是「不提」,而是「明确拒绝」。
不提是「随便」,q=0 是「别给我这个」。所以服务端退到了 identity,传输 1620 字节,一点没压。
三、那条铁律:你不提我就不压
客户端一个都不提时,服务端必须发不压缩的。Demo 第一组就是这样:1620 字节原样传。 这条规矩是踩出来的。 早年有服务端「看到请求头里有 Accept-Encoding 就压」,甚至不看内容, 结果把只认明文的客户端直接打挂。所以规矩改成「不主动压」,宁可多传几个字节也不猜。 压缩比这里很夸张——1620 压到 64 字节,只占 4%。 但那是因为正文是同一句话重复六十遍,属于压缩算法最爱的输入。 真实网页的 gzip 压缩比一般在 3 到 5 倍之间,不会有 25 倍这么好看。
四、动手 Demo:六组取值,看服务端选谁
1620 字节的正文,六种 Accept-Encoding 各请求一次,服务端按 q 值排序挑第一个自己支持的, 再算出压缩后的实际字节数。
cands = [(n, q) for n, q in cands if q > 0]
cands.sort(key=lambda x: -x[1]) # 按 q 从大到小
pick = u'identity'
for n, q in cands:
if n in (u'gzip', u'deflate'):
pick = n
break
body = enc(pick)
hdrs = [(u'Vary', u'Accept-Encoding')]
if pick != u'identity':
hdrs.append((u'Content-Encoding', pick))
实跑输出:
--------------------------------------------------------------------
Accept-Encoding 与 q 值:压缩格式怎么选
--------------------------------------------------------------------
原文 1620 字节
【不发】
Accept-Encoding (没发)
服务端选了 identity
响应 Content-Encoding identity
传输字节 1620(原 1620,100%)
【gzip】
Accept-Encoding gzip
服务端选了 gzip
响应 Content-Encoding gzip
传输字节 64(原 1620,4%)
【gzip 加 deflate】
Accept-Encoding gzip, deflate
服务端选了 gzip
响应 Content-Encoding gzip
传输字节 64(原 1620,4%)
【只要 deflate】
Accept-Encoding deflate
服务端选了 deflate
响应 Content-Encoding deflate
传输字节 52(原 1620,3%)
【gzip 权重低】
Accept-Encoding gzip;q=0.3, deflate;q=0.9
服务端选了 deflate
响应 Content-Encoding deflate
传输字节 52(原 1620,3%)
【明确拒绝 gzip】
Accept-Encoding gzip;q=0, identity
服务端选了 identity
响应 Content-Encoding identity
传输字节 1620(原 1620,100%)
服务端选压缩格式的规则是「按 q 从大到小,挑第一个我支持的」。
所以 `gzip;q=0.3, deflate;q=0.9` 会选 deflate,因为客户端明说更想要 deflate。
`gzip;q=0` 是明确拒绝——注意它和「不提 gzip」不一样:
不提是「随便」,q=0 是「别给我这个」。
一个都不提时选 identity,也就是不压缩。
这是历史包袱:早年有服务器一看到 Accept-Encoding 就无脑回 gzip,
哪怕客户端没说要,结果把只认明文的客户端打挂。所以规矩变成「你不提我就不压」。
不发:identity,1620 字节,100%。服务端不主动压。
gzip:64 字节,4%。压缩 25 倍。
gzip, deflate:还是 gzip,64 字节。两个 q 都是 1,按客户端给的顺序取第一个。
只要 deflate:52 字节,3%——比 gzip 还小一点,这是 zlib 和 gzip 头部差异造成的。
gzip;q=0.3, deflate;q=0.9:选 deflate,52 字节。q 值真的生效了。
gzip;q=0, identity:identity,1620 字节。q=0 就是明确拒绝。
30 秒实验
- 加一个
br的候选,看本机的 zlib 能不能造出来(不能,需第三方库)。 - 把正文换成一段真实 HTML,看压缩比掉到多少。
- 想一想:如果服务端不管客户端发什么都压,会打挂哪类客户端?
常见误区
- 「q 值是百分比」——是 0 到 1 的权重。
- 「q=0 等于没提」——是明确拒绝,比「没提」强得多。
- 「压缩比越高越好」——压得越狠 CPU 花得越多,小响应用 br 反而更慢。
自测三问(点开对答案)
1. `gzip;q=0` 和「不提 gzip」有什么区别? —— 前者是明确拒绝,后者是「随便,我不表态」。 2. 客户端什么都不发时,服务端该压吗? —— 不该。规矩是「不提就不压」,避免打挂只认明文的客户端。 3. `Content-Encoding` 会在什么时候出现? —— 响应正文真的被压缩时才出现,值为 gzip/deflate/br 等。编码说完,下一个还是协商:这次协商的是语言。
- ☐ (本节完,下一节 6.4.4 Accept-Language)