ngx_http_chunked_filter_module 是 Nginx 实现 HTTP/1.1 分块传输编码的核心模块,仅在 HTTP 版本 ≥ 1.1、未设 Content-Length、状态码允许 body 且未禁用 chunked 时触发,通过重写 chain_t 链表插入 chunk header(如 "X\r\n")、trailer("\r\n")及终止标记 "0\r\n\r\n",不拷贝数据而注入边界信息,依赖 filter 链顺序与内存管理协同工作。

nginx 的 ngx_http_chunked_filter_module 是实现 HTTP/1.1 分块传输编码(Chunked Transfer Encoding)的核心模块,它在响应体不带 Content-Length 且启用 chunked 编码时自动介入,将响应数据按块封装并追加 0\r\n\r\n 结束标记。它的逻辑并不复杂,但嵌入在 filter 链中、与内存管理及上下游协同紧密,理解其源码有助于排查响应截断、头部错乱、大文件流式输出异常等问题。
何时触发 chunked 编码
该模块不是默认始终启用的,它只在满足以下全部条件时才参与处理:
- 请求使用的 HTTP 版本 ≥ 1.1(
r->http_version >= NGX_HTTP_VERSION_11) - 响应未显式设置
Content-Length头(r->headers_out.content_length_n == -1) - 响应状态码允许 body(如非 1xx/204/304)且未禁用 chunked(
r->chunked为 1) - 上游未自行完成 chunked 封装(例如某些 upstream 模块已写好 chunk header)
注意:r->chunked = 1 通常由 ngx_http_set_chunked 设置,发生在 header 过滤阶段(如 ngx_http_header_filter)检测到无 Content-Length 时。
核心过滤逻辑:ngx\_http\_chunked\_filter
该函数是模块主入口,被注册为 body filter,在每个 output chain buffer 到达时调用。它不直接操作 socket,而是重写 chain_t 链表结构,插入 chunk header 和 trailer:
- 对首个非空 buffer,前置插入形如
"X\r\n"的 chunk size 行(十六进制 + CRLF) - 对每个 buffer 末尾追加
"\r\n"(chunk data 后的 CRLF) - 在整条响应结束时(收到空 buffer 或 last_buf 标志),插入终止块
"0\r\n\r\n" - 若遇到
flush标志或last_buf,立即尝试发送当前链表,避免缓冲堆积
关键细节:它不拷贝原始数据,而是通过构造新的 ngx_buf_t(指向栈上字符串或常量内存)并调整 chain_t 指针关系来“注入”chunk 边界信息。所有 chunk header 均分配在 request pool 中,生命周期与请求一致。
与内存和 buffer 管理的协作
chunked filter 对 buffer 类型敏感,会跳过某些特殊 buffer:
- 忽略
buf->in_file == 1的文件 buffer(不处理 sendfile 场景,chunked 不适用于纯 sendfile) - 跳过
buf->sync或buf->last_buf但尚未结束的 buffer(交由后续逻辑判断) - 当一个 buffer 同时含
last_buf和last_in_chain,即视为响应终结,立即添加"0\r\n\r\n"
若响应中途出错(如 upstream timeout),模块依赖 ngx_http_finalize_request 清理未发送的 chunk header,避免客户端等待超时。这也意味着:手动调用 ngx_http_send_special(r, NGX_HTTP_LAST) 时,必须确保此前所有 body 已经过 chunked filter,否则结尾 chunk 可能丢失。
调试与常见问题定位
遇到 chunked 相关异常(如浏览器卡在 loading、curl 报 transfer closed with outstanding read),可从以下方向切入:
- 开启
error_log debug;,搜索"http chunked"日志,确认模块是否被调用及插入 header 的位置 - 用
tcpdump或 Wireshark 抓包,检查响应流中是否缺失"0\r\n\r\n"或 chunk size 格式错误(如十进制、多出空格) - 检查是否有其他 filter(如 gzip、slice)顺序错误——chunked filter 必须在压缩 filter 之后(否则压缩后长度未知,无法 chunk),但在 write filter 之前
- 自定义模块若直接操作
r->out或绕过 filter 链写 body,极易破坏 chunked 结构,应统一走ngx_http_output_filter
不复杂但容易忽略:chunked 是协议层封装,它不改变业务数据,只负责让长度未知的流可被 HTTP 客户端正确解析。理解它如何穿插在 filter 链中,比记住每行代码更重要。










