同样是4096的表,HTTP/3比HTTP/2早678字节就把SEO红利丢光了
本文目录
- 把47个站的握手参数拉出来,68%宣告的动态表是0
- 先证明这个0不是解析错误
- 按CDN归属看,答案更整齐
- 顺手量到的另一件事:13个站没说自己能收多大的头
- 为什么HTTP/3的默认值是0,而HTTP/2的同一个默认值是4096?
- 规范这么定不是拍脑袋
- 动态表是0,到底差多少?
- 先说这组数据怎么保证是真的
- 然后是那条曲线
- 中间那几档还藏着一个更别扭的现象
- 在真实nginx上量一遍,第二个请求就掉到18字节
- 跟本地模拟对一对,还发现一个差异
- 同样是4096的表,为什么QPACK比HPACK早678字节就没了红利?
- 678字节是什么概念
- 那条断崖为什么不是斜坡?
- 为什么QPACK的那条线更靠前
- 拆分Cookie这一招,在HTTP/3上还灵吗?
- 过线之前拆,是纯亏
- 过线之后拆,能救回一大截
- 所以这一招的正确用法是有前提的
- 服务器日志能不能拿来算这笔账?
- 这笔账该怎么处理?
- 第一类:Cookie已经越过2400字节的电商站
- 第二类:自己管服务器、且真的开了HTTP/3的站
- 第三类:在CDN后面、以为切了协议就自动更快的站
- 一张对照表收尾
- 常见问题解答
- 我怎么知道我的站宣告的动态表容量是多少?
- 动态表容量能在nginx里配置吗?
- Cloudflare把它设成0,我的站是不是变慢了?
- 那我干脆不用HTTP/3,回到HTTP/2是不是更好?
- 为什么第一个请求那么大,之后就变成十几个字节?
- 把Cookie拆成多个字段,会不会影响服务端读取?
- 这件事对SEO到底有多大影响?
- 权威参考资料
摘要:把47个能连上HTTP/3的站的握手参数逐个拉出来,其中32个宣告的QPACK动态表容量是0,占68.1%,Cloudflare后面那24个站无一例外。表是0意味着请求头的复用能力被完全关掉:一份2000字节的Cookie连发20个请求,表开着均摊191字节,表是0就是1786字节,差9.3倍。更意外的是同为4096的表,QPACK的红利在Cookie长到2394字节时就断了,而HPACK撑到3072——同一个数字的表,HTTP/3比HTTP/2早678字节交卷。这不是谁实现得差,是RFC 9114明写的取舍:拿压缩效率换延迟。
上一篇拆的是能不能走到HTTP/3上去——结论是一次访问里的第一个请求走不到,而爬虫抓页面通常只发那一个请求。
那篇写完,保哥心里有个疙瘩没解开:对于那些确实走到了HTTP/3上的请求,它们到底拿到了什么?
这个问题看起来不该有悬念。HTTP/3是HTTP/2的后继者,头部压缩这块用的是QPACK,而QPACK是照着HPACK改的。按常识推,同一件事上HTTP/3应该不比HTTP/2差。
结果一测,两个数字都跟常识反着来。
把47个站的握手参数拉出来,68%宣告的动态表是0
先说数据是怎么拿到的,因为这一步决定了后面所有结论的可信度。
HTTP/3的连接建立起来之后,双方会各自在控制流上发一个SETTINGS帧,把自己这一侧的参数告诉对方。这里面有一个参数叫 SETTINGS_QPACK_MAX_TABLE_CAPACITY,它规定了对端可以用多大的动态表来压缩发给我的头。
保哥用一个纯Python的QUIC客户端,对125个域名逐个发起真实的h3握手,把服务端发过来的SETTINGS帧解出来。49个握上了手,47个拿到了完整的SETTINGS。分布如下:
| QPACK动态表容量 | 站数 | 占比 |
|---|---|---|
| 0(没宣告,按规范默认值算) | 32 | 68.1% |
| 4096 | 5 | 10.6% |
| 16384 | 1 | 2.1% |
| 65536 | 9 | 19.1% |
68.1%这个数字第一眼看上去像是我解析错了。所以在往下写之前,得先证明这个0是真的。
先证明这个0不是解析错误
对账方式是找一个已知答案的对照物:保哥在自己的服务器上起了一个nginx 1.28.3的实例,开着HTTP/3,用同一个客户端去连,看读出来的是什么。
读出来是 QPACK_MAX_TABLE_CAPACITY = 4096、QPACK_BLOCKED_STREAMS = 128。这两个数正是nginx源码里写死的出厂值。同一段解析代码,在nginx上读出4096,在Cloudflare上读出没有这一项——那就是真的没有。
再看另外两个参数的分布,也能佐证解析是对的:
| 参数 | 取值分布 |
|---|---|
QPACK_BLOCKED_STREAMS | 0 → 32站;100 → 10站;128 → 3站;64与512各1站 |
MAX_FIELD_SECTION_SIZE | 131072 → 23站;65536 → 6站;32768 → 4站;1048576 → 1站;未宣告13站 |
注意第一行:宣告表容量为0的那32个站,BLOCKED_STREAMS 也全是0。这两个参数是配套的——表都不给,自然也不会允许流被压缩阻塞。32和32完全重合,这个内部一致性说明这批站是明确关掉了这套机制,不是漏配了某一项。
按CDN归属看,答案更整齐
| 服务器与CDN | 站数 | 表容量取值 |
|---|---|---|
| Cloudflare | 24 | 全部为0 |
| Google Frontend | 4 | 65536三个,0一个 |
| nginx | 5 | 4096两个、65536两个、0一个 |
| Tengine(阿里系) | 2 | 16384、4096 |
| 腾讯EdgeOne | 1 | 4096 |
Cloudflare那一行是24比0,一个例外都没有。这不是配置疏漏,是产品层面的统一决策。
而Google Frontend那边3个站给了65536——64KB的动态表,是nginx出厂值的16倍。同一件事,两家全球最大的边缘网络给出了完全相反的选择。
顺手量到的另一件事:13个站没说自己能收多大的头
既然SETTINGS帧都解出来了,另一个参数也顺带看了:MAX_FIELD_SECTION_SIZE,服务端用它告诉客户端我最多接受多大的一份请求头。
| 取值 | 换算 | 站数 |
|---|---|---|
| 131072 | 128 KB | 23 |
| 65536 | 64 KB | 6 |
| 32768 | 32 KB | 4 |
| 1048576 | 1 MB | 1 |
| 没宣告 | 视为无限制 | 13 |
这一栏跟之前在HTTP/1.1与HTTP/2上量到的那道墙正好接上。那次现网普查测出来的阈值中位数在16KB附近,最低的一个站6912字节就开始拒绝。
而这里HTTP/3这一侧,多数站给的是128KB——比TCP那一侧宽了将近8倍。更微妙的是有13个站压根没宣告这个参数,按规范就是不设限。
把两组数放一起,会得到一个挺古怪的结论:同一个站,同一份超大的请求头,走HTTP/2可能被拒,走HTTP/3可能就过了。这也解释了那类最难缠的报障——同一个用户,有时候能打开,有时候打不开,而两次的区别只是浏览器那一刻选了哪条路。
为什么HTTP/3的默认值是0,而HTTP/2的同一个默认值是4096?
这里得回到规范。RFC 9204第5节对这个参数的说明只有两句话,但这两句话把整件事说透了:
SETTINGS_QPACK_MAX_TABLE_CAPACITY (0x01): The default value is zero. See Section 3.2 for usage. This is the equivalent of the SETTINGS_HEADER_TABLE_SIZE from HTTP/2.
两个信息点:默认值是零,以及它等价于HTTP/2的SETTINGS_HEADER_TABLE_SIZE。
而HTTP/2那个参数的默认值,写在RFC 9113里,是 4096。
| HTTP/2(HPACK) | HTTP/3(QPACK) | |
|---|---|---|
| 参数名 | SETTINGS_HEADER_TABLE_SIZE | SETTINGS_QPACK_MAX_TABLE_CAPACITY |
| 规范默认值 | 4096 | 0 |
| 什么都不配的后果 | 压缩正常工作 | 动态表完全不可用 |
规范自己说这两个参数等价,却给了两个相反的默认值。这一条是整篇文章里最值得记住的:同一个机制从HTTP/2迁到HTTP/3,默认状态从开着变成了关着。
规范这么定不是拍脑袋
动态表在QPACK里比在HPACK里危险得多。HPACK跑在TCP上,数据严格按序到达,编码器往表里插一条、解码器就一定先收到那一条。QUIC是乱序的,装着表更新指令的那个包可能比引用它的那个请求晚到——解码器拿到一个引用,发现表里还没这一项,只能把整条流挂起来等。这就是队头阻塞,而HTTP/3存在的全部理由就是消灭它。
所以规范把默认值定成0,等于说:你要用这个提速功能,得自己明确开口要,因为它有可能把HTTP/3最大的优点抵消掉。
Cloudflare那24个站的选择,放在这个背景下就完全说得通了。作为一个要面对全世界各种糟糕网络的边缘网络,它选择了保延迟、放弃压缩率。这是个理性的决定——只是站长这一侧多半从来不知道自己在这笔交易里被替着签了字。
动态表是0,到底差多少?
知道有这个开关是一回事,知道它值多少钱是另一回事。
保哥用QPACK的编码器实现直接量了一遍:构造一份典型的浏览器请求头(12个字段,含一份2000字节的Cookie),在同一条连接上连发20个请求,只改动态表容量这一个变量。
先说这组数据怎么保证是真的
编码器算出来的字节数,如果没人验证它能被还原成原来的头,那就只是一串数字。所以每一轮编码都同时跑一个真实的QPACK解码器,把编码器流和解码器流双向喂回去,再把解码出来的头字段跟输入的逐条比对。
全部档位、全部请求,解码结果与输入不一致的次数是 0。这组数才敢往下用。
然后是那条曲线
| 动态表容量 | 20个请求合计 | 均摊每请求 |
|---|---|---|
| 0 | 35720 B | 1786.0 B |
| 256 | 34570 B | 1728.5 B |
| 1024 | 30833 B | 1541.7 B |
| 2048 | 30774 B | 1538.7 B |
| 3072 | 31167 B | 1558.3 B |
| 4096 | 3829 B | 191.4 B |
| 8192 | 3829 B | 191.4 B |
| 65536 | 3829 B | 191.4 B |
从0到3072,均摊字节从1786慢慢降到1538再回升到1558——基本没动。从3072到4096,一步从1558掉到191.4。差8.1倍,而且此后再加表容量一个字节都不再省。
把两端接起来看:动态表0与4096之间,同样这20个请求,差 9.3倍。
这条曲线的形状很重要:它不是斜坡,是台阶。给了一半的表容量,拿到的不是一半的收益,是零收益。这一点在下面第六节会解释原因。
中间那几档还藏着一个更别扭的现象
把表容量从1024加到2048再加到3072,均摊字节是1541.7 → 1538.7 → 1558.3。加到3072那一档,反而比2048那一档多花了20字节。
这不是测量误差——每一档都跑了20个请求、都做了解码校验、结果可重复。它反映的是一件很实际的事:
- 表容量不够装下那条最大的Cookie时,Cookie本身进不了表
- 但那些小字段(
accept-language、sec-fetch-*这些)还是会进去 - 进表要发编码器流上的指令,指令本身也占字节
- 表越大,塞进去的小字段越多,指令开销越大,而真正吃字节的那条大Cookie一次都没省到
于是就出现了这个局面:给一点点表容量,比一点不给还差。付了管理成本,没拿到主要收益。
这一条对做技术选型的人有实际意义:如果哪天这个参数变得可配置了,不要想着折中给个中间值。要么给足到能装下最长那个字段,要么干脆给0——中间地带是最贵的。
在真实nginx上量一遍,第二个请求就掉到18字节
编码器层面的数字再干净,也得回到真实服务器上验证。
保哥在服务器上起了一个独立的nginx实例(不碰生产站),开着HTTP/3,用Lua把服务端实际收到的东西回显出来:协议版本、Cookie的实际字节数、nginx记录的请求长度。然后在同一条h3连接上连发12个请求。
| Cookie长度 | nginx记录的请求长度序列 |
|---|---|
| 2000字节 | 1706 → 22 → 18 → 18 → 18 → 18 → 18 → 18 → 18 → 18 → 19 → 19 |
| 529字节 | 586 → 18 → 18 → 18 → 18 → 18 → 18 → 18 → 18 → 18 → 19 → 19 |
第一个请求1706字节,第二个22字节,之后一路18。降了95倍。而这12个请求里,服务端回显的Cookie字节数全程是同一个数,一个字节都没少——内容一样,只是不用再重发一遍了。
跟本地模拟对一对,还发现一个差异
把真实nginx的序列和本地编码器的模拟放在一起:
| 来源 | 第1个 | 第2个 | 第3个 | 之后 |
|---|---|---|---|---|
| 本地QPACK模拟(表4096) | 1786 | 1791 | 14 | 14 |
| 真实nginx(表4096) | 1706 | 22 | 18 | 18 |
| 本地HPACK模拟(表4096) | 1780 | 12 | 12 | 12 |
本地模拟里,QPACK的第二个请求还是全量的1791字节,第三个才降下来;真实nginx上第二个就降到了22。原因是编码器要不要冒险引用一条还没被对端确认的表项,是个策略问题——保守的实现会等确认,激进的实现敢赌。
这个差异本身是个方法论提醒:协议库的模拟能算出上界和下界,但具体落在哪儿要看服务端的实现策略。两条路都跑一遍,对不上的地方恰恰是最值得看的地方。
顺带注意HPACK那一行:它第二个请求就到12字节,比QPACK少一轮预热,稳态字节数也更小。这已经是第二个HTTP/3不如HTTP/2的地方了。
同样是4096的表,为什么QPACK比HPACK早678字节就没了红利?
这是本文最反直觉的一条,也是保哥没料到的一条。
之前量HTTP/2的时候发现过一条界线:Cookie头长到3072字节附近,HPACK的动态表红利就归零了。原因是条目大小算的是字段名长度加字段值长度再加32字节,太大的条目塞不进4096的表。
那么QPACK呢?规范里条目大小的算法是一模一样的:
The size of an entry is the sum of its name's length in bytes, its value's length in bytes, and 32 additional bytes.
同样的算法、同样的4096,界线应该在同一个位置。保哥用二分法扫了一遍,结果是:
| 压缩机制 | 表容量 | 红利消失的Cookie长度 |
|---|---|---|
| HPACK(HTTP/2) | 4096 | 约 3072 字节 |
| QPACK(HTTP/3) | 4096 | 约 2396 字节 |
二分的精确落点是:Cookie长2394字节时还有4.75倍红利,长到2398字节就只剩1.38倍。4个字节之内翻脸。
也就是说,同一个数字的表、同一条条目大小公式,QPACK比HPACK早678字节就交卷了。
678字节是什么概念
放到真实的Cookie尺寸上看这个差距。保哥在实验台上用真实Chrome访问了一次,浏览器带过来的本站Cookie是529字节——这是一个内容站的量级。而电商站是另一个世界:之前测过的某个化妆品站,在真实Chrome里是41条、4176字节。
| 站点类型 | 典型Cookie长度 | HPACK 4096 | QPACK 4096 |
|---|---|---|---|
| 内容站/博客 | 500上下 | 有红利 | 有红利 |
| 轻量商城 | 2000上下 | 有红利 | 有红利 |
| 装了3到5个营销工具的独立站 | 2400到3000 | 还有红利 | 已经没了 |
| 大型电商 | 4000以上 | 都没有 | 都没有 |
中间那一档正好卡在两条界线之间。这一档的站,从HTTP/2切到HTTP/3的那一刻,请求头压缩这笔账会变差,而没有任何一个监控指标会告诉他们。
那条断崖为什么不是斜坡?
把上面两组现象放在一起看,其实是同一个机制的两个切面:表容量给一半没有一半的效果,Cookie长一点点就从有红利变成没红利。两处都是台阶不是斜坡。
原因在于动态表的工作方式是全有或全无的。一条Cookie只有两种命运:
- 塞得进表:第一次发全文,之后每次发一个索引,几个字节
- 塞不进表:每一次都发全文,只有Huffman编码能省一点
没有中间状态。Cookie是一个不可分割的字段,它要么整条进表,要么整条不进。所以只要它的条目大小(值长度加名字长度再加32)越过了实现愿意分配的那条线,收益就直接归零,而不是打个折。
为什么QPACK的那条线更靠前
规范里给了方向性的解释。RFC 9114第4.2.1节介绍QPACK时写的是:
[QPACK] describes a variation of HPACK that gives an encoder some control over how much head-of-line blocking can be caused by compression. This allows an encoder to balance compression efficiency with latency.
关键词是 balance compression efficiency with latency——在压缩效率和延迟之间做平衡。规范没有承诺QPACK的压缩效率不低于HPACK,它明说了这是一笔交易。
具体到实现层面,QPACK编码器要给未来的表更新留出余量:一条超大的条目一旦插进去,会把表里已有的其他条目全部挤出去,而那些条目正被其他并发的流引用着。所以编码器实现会比HPACK更保守地判断这条值不值得插。678字节这个差值,就是保守程度的实测体现。
本文只描述这个现象和它的位置,不去猜具体某个实现内部的阈值公式。对做站的人来说,能记住的判据是一句话:请求头里最长那个字段,加32之后如果超过表容量的六成,在HTTP/3上就别指望压缩了。
拆分Cookie这一招,在HTTP/3上还灵吗?
HTTP/2那边有一个很好使的办法:把一条超长的Cookie拆成多个 cookie 字段发出去,每条都变小了,就都能进表。RFC 9114在HTTP/3上也明确允许这么干:
To allow for better compression efficiency, the Cookie header field ([COOKIES]) MAY be split into separate field lines, each with one or more cookie-pairs, before compression.
保哥把这招在QPACK上试了一遍,10个请求一组,同样带解码校验:
| Cookie总长 | 不拆 | 拆2条 | 拆3条 | 拆4条 | 拆6条 |
|---|---|---|---|---|---|
| 1000 B | 2157 | 2174 | 2187 | 2202 | 2226 |
| 2000 B | 3689 | 3937 | 4302 | 4145 | 4309 |
| 3000 B | 23404 | 13592 | 11012 | 10176 | 11751 |
| 4000 B | 30714 | 18821 | 22109 | 18658 | 18527 |
这张表得竖着读,因为上下两半的结论是相反的。
过线之前拆,是纯亏
1000字节和2000字节这两行,拆得越碎总字节越多。1000字节从不拆的2157一路涨到拆6条的2226;2000字节更明显,从3689涨到4309,多花了17%。
原因不难理解:原来一条能进表的东西,拆开之后变成好几条,每一条都要占一个索引位、都要在表里单独记一份,管理成本比省下来的多。
过线之后拆,能救回一大截
3000字节这一行是全表最戏剧的:不拆是23404字节,拆成4条降到10176,省了2.3倍。4000字节那一行也一样,拆2条就从30714掉到18821。
逻辑很简单:整条塞不进表的东西,切小之后每一块都塞得进去了,红利就回来了。
所以这一招的正确用法是有前提的
| 你的Cookie总长 | 该不该拆 | 理由 |
|---|---|---|
| 低于2400字节 | 不要拆 | 本来就有红利,拆了净亏 |
| 2400到3000 | 拆3到4条 | 把每条压回能进表的尺寸 |
| 超过3000 | 先减肥,再考虑拆 | 拆只是止损,减掉才是治本 |
要提醒一句:这个动作的实施位置不在服务器上,而在客户端或者边缘。服务器改不了浏览器怎么发Cookie。真正能落地的手段还是把Cookie本身减下来——干掉一条1500字节的,比删四十条小的都管用。
服务器日志能不能拿来算这笔账?
不能。而且它给出的错误答案,方向是反的。
保哥在实验台上做了一次干净的对照:真实Chrome访问同一个地址两次,第一次走HTTP/2、第二次走HTTP/3,服务端把收到的东西全部回显出来。
| 指标 | 第1次(HTTP/2) | 第2次(HTTP/3) |
|---|---|---|
| 服务端收到的Cookie字节 | 529 | 529 |
| 头字段条数 | 14 | 13 |
| nginx记录的请求长度 | 884 | 30 |
Cookie回显两次都是529,说明浏览器发的东西一样、服务器收的东西也一样。但nginx的请求长度变量,一个记884、一个记30,差29倍。
再看另一组,用可控的客户端逐档发不同长度的Cookie:
| 构造的Cookie长度 | HTTP/2记的请求长度 | HTTP/3记的请求长度 |
|---|---|---|
| 200 | 295 | 337 |
| 1000 | 1095 | 931 |
| 2000 | 2095 | 1697 |
| 4000 | 4095 | 3202 |
这一组的Cookie回显在全部6个档位上跟构造值分毫不差,所以数据本身是可信的。但两列数字的含义并不一样:HTTP/2那列基本贴着原始长度(curl每次新建连接,压缩没机会生效),HTTP/3那列已经是QPACK编码后的字节。
结论很直接:这个变量在两种协议下记的不是同一个东西,任何跨协议的横向对比都是错的。如果一个站从HTTP/2切到HTTP/3,运维看日志会以为上行流量降了一大截,实际上用户发的字节一点没变,只是账本换了记法。
这类问题保哥碰到过好几次了,形状都一样:换一个工具去查同一件事,答案就变了。判据永远是同一条——让被测那一方把它实际收到的东西回显出来,跟你发出去的对账,而不是相信中间任何一个环节的记账。
这笔账该怎么处理?
先说一句让人放松的:对绝大多数内容站,这件事的实际影响接近于零。500字节量级的Cookie,在哪种压缩机制下都在红利区内,动态表是0也就是每个请求多几百字节,对一个正常页面来说不值一提。
真正要认真对待的是这三类站。
第一类:Cookie已经越过2400字节的电商站
这一类是唯一会被HTTP/2与HTTP/3那678字节差值真实咬到的。判据很简单,在浏览器控制台里跑一行:
document.cookie.length这个数不含HttpOnly的那些,所以真实值还要更大。超过2000就该去数一数每一条各占多少了。减Cookie的正确顺序是先揪最大的那三五条,而不是删条数。
第二类:自己管服务器、且真的开了HTTP/3的站
nginx出厂给的是4096,够用但不宽裕。如果你的站请求头本来就重,这个值目前没有暴露成可配置项——这也是选择HTTP/3时该知道的一个约束。nginx这类配置改完不报错的地方特别多,凡是文档里没写成指令的参数,都不要假设自己能调。
第三类:在CDN后面、以为切了协议就自动更快的站
这一类人数最多。Cloudflare那24个站的表容量全是0,意味着在它后面的站,HTTP/3的头部压缩红利这一项从来就没打开过,而这个决定不在站长手里,控制台上也找不到对应的开关。
这不是说Cloudflare做错了——用压缩率换掉队头阻塞风险,对一个要服务全球各种网络的边缘网络是合理的取舍。但站长该知道自己拿到的是哪一半:你拿到的是HTTP/3的延迟特性,没拿到它的压缩特性。
一张对照表收尾
| 你想要的 | HTTP/3给不给 | 取决于 |
|---|---|---|
| 丢包时不拖累其他请求 | 给 | 协议本身,无需配置 |
| 切换网络时连接不断 | 给 | 协议本身,无需配置 |
| 请求头只发一次 | 看服务端脸色 | 对端宣告的动态表容量 |
| 爬虫抓的那份HTML也走h3 | 基本不给 | 发现机制的限制 |
两篇合起来的账是这样的:HTTP/3在传输层给的那些好处是真的、而且免费;但它在应用层许诺的那笔压缩红利,68%的站上默认关着,剩下的还要看请求头有没有超过2400字节。而这两样东西,爬虫抓的那一个请求一样都碰不到。
常见问题解答
我怎么知道我的站宣告的动态表容量是多少?
需要一个能连HTTP/3并读出SETTINGS帧的客户端。系统自带的curl多半没编译HTTP/3支持(用 curl --version 看Features那行有没有HTTP3字样)。Python生态里有纯软件实现的QUIC库可以做这件事,几十行代码就能把SETTINGS打出来。浏览器开发者工具看不到这个参数,它只显示协议名。
动态表容量能在nginx里配置吗?
目前不能。nginx的HTTP/3模块把这个值写死在源码里,出厂是4096,配置文件里没有对应的指令。所以自建nginx的站在这一项上没有调节余地,好在4096对多数场景够用。
Cloudflare把它设成0,我的站是不是变慢了?
请求头这一侧确实少了一笔可观的节省,但整体是快是慢要看你的请求头有多重。Cookie在500字节以内的站,这笔差价小到测不出来;Cookie到了两三千字节的站,每个请求要多发一两千字节的上行——而上行带宽在移动网络上通常只有下行的几分之一。判据是先量自己的Cookie有多大,而不是先判断CDN的选择对不对。
那我干脆不用HTTP/3,回到HTTP/2是不是更好?
不必这么极端。HTTP/3在丢包环境下的表现、连接迁移这些好处是实打实的,而且不需要任何配置。压缩这一项只是它没兑现的那部分。正确的做法是把请求头本身减下来——Cookie小了,两种协议下都省,而且对第一个请求也有效。
为什么第一个请求那么大,之后就变成十几个字节?
因为动态表要预热。第一个请求必须把每个字段的完整内容发过去,同时告诉对端把它们记进表;从第二个请求开始,只发一个索引号就够了。这也意味着只发一个请求就断开的客户端,永远只付得起第一次那个价钱,压缩机制对它毫无意义。
把Cookie拆成多个字段,会不会影响服务端读取?
不会。RFC 9114规定解码之后必须把多个cookie字段用 ; 拼回一条,再交给应用。所以后端代码读到的还是完整的一条,跟没拆一样。但这个动作只能在客户端或边缘做,服务器改不了浏览器发头的方式。
这件事对SEO到底有多大影响?
直接影响很小,因为爬虫多半连不到HTTP/3上。间接影响落在真实用户的体验指标上——上行字节少了,弱网环境下的请求发出得更快。把它当成一项用户体验优化来做,别指望它改善抓取。真要改善抓取,力气应该花在响应时间和缓存命中上。
权威参考资料
本文标题:《同样是4096的表,HTTP/3比HTTP/2早678字节就把SEO红利丢光了》
本文链接:https://zhangwenbao.com/qpack-dynamic-table-zero-http3-header-compression-seo.html
版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0