传输协议版本和响应内容编码是两项需要分别检查的事实。

gzip、HTTP/2 与 Brotli 需要分别验证

HTTP/2 改变 HTTP 传输分帧和多路复用方式;gzip 与 Brotli 编码的是表示数据。启用 HTTP/2 不会自动压缩响应正文,启用 gzip 也不能证明客户端协商出了 HTTP/2。HTTP/2 的头部压缩又是另一种机制。可靠检查应分别记录协商协议、响应 Content-Encoding、传输正文字节数,以及实际解码后得到的内容,不能用其中一项代替其他结果。 坏掉的样子 发送 Accept-Encoding: gzip, br,然后把结果保存成 .br,并不能证明使用了 Brotli。服务器可能选择 gzip,也可能发送仍被请求接受的原始表示。真实编码由响应头说明。根据请求列表或输出文件名猜测,会导致使用错误解码器,也可能让报告声称获得了实际上根本没有发生的压缩结果。文件名只是本地命名,不是协议证据。 Brotli 支持还属于部署能力,并不是安装 nginx 后自然具备的功能。Brotli 过滤和预压缩文件处理由独立模块提供,需要针对实际二进制与配置确认可用性、构建兼容性。添加不受支持的指令可能让配置验证失败,而客户端声明接受 Brotli 不会替服务器安装功能。检查模块时,应以当前程序行为为准,不能从另一台机器的配置推断。 客户端自动解码会制造另一种测量陷阱。curl --compressed 收到较小编码正文后,可以保存成较大的解码文件。把这个文件大小与原始压缩输出比较,实际比较的是不同对象。反过来,只使用 --http2 却不检查协商结果,也可能把允许的协议回落误当成 HTTP/2 已经成功验证。命令选项表达请求意图,结果字段才说明本次发生了什么。 快速验证 将脚本保存为 check-h2-coding.py,准备 Docker、Python、OpenSSL 和支持 HTTP/2 的 curl 后运行。记录客户端为带 nghttp2 的 curl 8.7.1,服务器为 Linux arm64 上的 nginx 1.28.0。脚本生成短期本地证书,并通过 --cacert 显式信任该证书,只发布宿主回环端口,不需要连接真实公网服务: ...

一月 28, 2028 · 3 分钟 · XB Field Notes
Accept-Encoding 允许 gzip,Content-Encoding 标明所用编码,实际解压验证交付内容。

显式启用 gzip,再验证协商与解码后的字节

gzip 是响应内容编码:nginx 可以发送压缩表示,由接受该编码的客户端解码。启用它需要 gzip on;gzip_types 扩展有资格压缩的 MIME 类型,却不会单独激活压缩。最终结果取决于响应、请求方能力和实际生效配置。只有压缩后仍能正确还原目标内容,较小正文才有价值,不能把“传输字节变少”与“功能已经正确”直接画等号。 坏掉的样子 在 gzip off 下列出 MIME 类型,不会让响应变成 gzip。另一个常见遗漏是 gzip_vary on。nginx 并不会仅因为发生压缩,就无条件添加 Vary: Accept-Encoding;该行为受对应指令控制。共享缓存需要知道哪些请求头维度影响表示选择,否则不同解码能力的客户端可能拿到不合适的已存响应。压缩成功和缓存选择正确是两个需要分别检查的结果。 最小长度设置的契约比名称暗示的更窄。gzip_min_length 根据响应 Content-Length 字段确定长度,不一定知道未指定长度响应的最终大小。因此,一个很短的动态流式响应仍可能被压缩,而同样短的静态文件会因为已知长度低于阈值而跳过压缩。不能从阈值配置推断所有小响应都不会进入压缩路径。 测量方法也可能误导。解压后输出文件的大小,不是压缩传输正文大小;压缩正文字节也不包含全部 HTTP 分帧、响应头和 TLS 流量。.gz 等文件扩展名本身不能证明实际编码。解释任何表面上的带宽节省之前,都应检查真实 Content-Encoding,并解码核对字节,否则可能只是把两种不同计量对象放在一起比较。 快速验证 将脚本保存为 check-gzip.py,准备 Python、Docker 和固定镜像后运行。记录环境为 Linux arm64 上的 nginx 1.28.0,以及一个小型 Python 后端。nginx 显式开启三级 gzip,在内置 HTML 资格之外允许 JSON,设置 1,024 字节下限,并显式启用 Vary。服务只发布到宿主回环,实验完成后清理自己的资源: ...

八月 29, 2027 · 2 分钟 · XB Field Notes
同一份文档分别以压缩与未压缩两种形态提供。

压缩协商本质上仍是内容协商

HTTP 压缩选择的是施加了内容编码的表示,不是一种新的媒体类型。经过 gzip 的文本,解压后仍然是文本。因此有效验证需要回答三个问题:编码是否可接受、传输内容是否变小、解码后是否恢复了原本要提供的字节。配置里勾选了压缩,或者接口返回成功状态,都不能同时证明这三件事。 区分缺失、排除与偏好 根据 RFC 9110,Accept-Encoding 缺失时,任何内容编码都被认为可接受;显式空值则表示不希望使用内容编码。identity 代表没有内容编码,通常默认可接受,除非被 identity;q=0 或适用的 *;q=0 排除。通配符还可以让没有逐个列出的编码成为可接受候选,因此“只能发送客户端单独点名的编码”并不是完整规则。 质量值为零表示不可接受,正数则用于表达可接受编码之间的偏好。但服务器还必须具有对应编码器,并判断内容是否适合压缩。客户端偏好 Brotli,不会替服务器安装 Brotli。如果 identity 仍然可接受,未编码响应可能就是合理结果。即使所有候选都被排除,也不能假定每个服务器都会实施严格的 406 策略,因为 RFC 9110 还允许忽略协商偏好。应用需要哪种行为,必须单独验证。 坏掉的样子 代理压缩正文却漏写 Content-Encoding,客户端就可能把压缩字节当成文本;反过来,代理解压后保留原字段,则可能诱发第二次解码。Content-Length 如果存在,描述的是编码后内容的字节数,不是原始长度。流式压缩不一定提前知道最终大小,可以不发送这个字段,而不是继续沿用旧长度。消息分帧与内容编码是不同层次,不能混为一谈。 当 Accept-Encoding 参与可复用变体的选择时,Vary 需要描述这个维度,未编码响应也不能漏掉。否则,缓存中的 gzip 副本可能送给明确排除了 gzip 的客户端。缓存新鲜度和存储资格仍是另外的问题。即使选择正确,也不保证压缩值得:很小的正文存在额外开销,已经压缩过的媒体通常收益有限。应测代表性内容和处理成本,而不是声称所有文本都有一个固定的压缩百分比。 快速验证 准备 Python 3.11 或更新版本、支持 gzip 的 curl,以及 PATH 中的 Caddy 2.11.4。从官方发布页取得适合系统的二进制,并核对公布的校验和后再运行。程序使用回环端口 18765;若该端口占用,应在代码里换成空闲端口。它关闭管理端点、自动 HTTPS 和配置持久化,只清理自己创建的临时文件与子进程,不接触正在运行的网站服务。 ...

七月 21, 2026 · 3 分钟 · XB Field Notes