nginx gzip模块通过过滤链机制实现流式压缩:先由header过滤器判定启用并修改响应头,再由body过滤器基于zlib分块压缩响应体,维护上下文状态,支持flush与finish语义,并按比率回退未压缩数据。

要理解 Nginx 的 Gzip 模块数据流处理过程,核心是追踪 HTTP 响应体从生成到压缩、再到发送的完整生命周期。这需要结合模块注册机制、过滤链挂载顺序、内存缓冲管理以及 zlib 的实际调用逻辑来分析,而非仅看单个函数。
一、Gzip 模块如何被接入响应过滤链
Nginx 的输出过滤器链(ngx_http_top_body_filter)是响应体处理的核心路径。Gzip 模块通过 ngx_http_gzip_filter_module 在初始化阶段将自己的过滤函数 ngx_http_gzip_body_filter 插入该链首(或按优先级插入)。关键点在于:
- 模块在
ngx_http_gzip_filter_init中调用ngx_http_next_body_filter = ngx_http_top_body_filter,保存原链头,再将自身设为新链头; - 该过滤器仅对满足条件的响应启用:状态码为 2xx/3xx、Content-Type 匹配
gzip_types、未禁用(如含Vary: Accept-Encoding但客户端不支持 gzip)、且响应体非空; - 是否启用由
ngx_http_gzip_header_filter(header 过滤器)提前判定,并设置r->gzip_vary和r->gzip_ok标志位,供 body 过滤器读取。
二、压缩前的数据准备与缓冲区管理
Gzip 过滤器不直接操作原始响应 buffer,而是通过两级缓冲策略协调:
- 使用
ngx_http_gzip_ctx_t上下文结构维护 zlib 的z_stream实例、输入/输出缓冲区(in_buf/out_buf)、以及待压缩的链表in; - 每次收到上游传来的 buffer 链(
in),先检查是否需 flush(如遇到last_buf或flush_buf),再尝试填充输入缓冲区; - 输入缓冲区满(或遇 flush)时,调用
deflate()执行压缩,输出写入out_buf;若输出缓冲区满,则立即封装为ngx_buf_t并调用下游过滤器(即继续走ngx_http_next_body_filter); - 未压缩完的数据保留在
in链中,等待下次调用——这意味着一个 HTTP 响应体可能被分多次送入 zlib 流式压缩。
三、zlib 调用的关键细节与边界处理
Gzip 模块对 zlib 的封装并非简单调用,而是严格遵循流式压缩协议:
- 首次压缩前调用
deflateInit2(),指定 windowBits = 15 + 16(启用 gzip header),memLevel = 8,strategy = Z_FILTERED(适应文本); - 每次调用
deflate()时,avail_in和next_in来自当前 input buffer,avail_out/next_out指向 output buffer; - 当输入耗尽但输出缓冲未满时,若响应未结束(无
last_buf),不调用deflateEnd(),而是保留 stream 状态,等待下一批数据; - 收到
last_buf后,执行deflate(Z_FINISH)直至返回Z_STREAM_END,确保所有剩余数据写出,并在末尾补全 gzip trailer(CRC32 + ISIZE); - 若压缩后数据反而变大(如小文件或不可压缩内容),模块会丢弃压缩结果,回退使用原始未压缩 buffer(由
ngx_http_gzip_test_ratio控制,默认 ratio > 1.0 才接受压缩)。
四、Header 修改与响应一致性保障
压缩只是 body 处理,header 必须同步更新以符合 HTTP 协议:
-
ngx_http_gzip_header_filter在 header 发送前介入,添加Content-Encoding: gzip,并重写Content-Length(若已知长度); - 若原始响应含
Content-Length,压缩后长度未知,Nginx 会移除该 header,改用 chunked transfer encoding(自动添加Transfer-Encoding: chunked); - 同时添加
Vary: Accept-Encoding(除非显式关闭gzip_vary off),确保代理缓存能区分压缩与非压缩版本; - 所有 header 修改均发生在 body 过滤器执行前,避免 body 已开始输出而 header 锁定导致错误。
源码分析时建议以 src/http/modules/ngx_http_gzip_filter_module.c 为主,重点关注 ngx_http_gzip_header_filter、ngx_http_gzip_body_filter 和 ngx_http_gzip_filter_buffer 三个函数,配合 ngx_http_gzip_ctx_t 结构体生命周期跟踪数据流向。调试可加 ngx_log_debug 日志观察 buffer 拆分、zlib 返回值及 flush 时机。










