nginx 的 $senttrailer* 变量仅在后端严格遵循 http/1.1 分块传输规范(含 trailer 头、chunked 编码、0\r\n 后紧跟合法 trailer 字段)时才有效;否则为空。需用抓包验证原始响应流,并确保 nginx ≥ 1.13.2、正确配置 log_format 与 map 默认值。

在使用分块传输编码(Chunked Transfer Encoding)时,HTTP 响应主体由多个“块”组成,末尾以一个长度为 0 的空块结束。但标准 HTTP/1.1 并不支持在 0\r\n 之后附加任意数据——这部分若存在,属于非标准行为,通常由特定中间件(如某些定制 Nginx 模块、Envoy 插件或自研代理)扩展实现,用于携带业务级元数据(如签名、耗时、trace_id 等)。$sent_trailer_名称 是 Nginx 中的内置变量,专门用于读取响应 trailers(即分块结束后的额外头部字段),前提是后端明确通过 Trailer 响应头声明,并在响应体末尾发送这些 trailer 字段。
确认后端是否真正发送了 Trailer
Trailer 不是默认启用的机制。必须满足三个条件:
- 后端响应头中包含
Trailer: X-Request-Duration, X-Signature(列出你期望的 trailer 名称) - 响应使用
Transfer-Encoding: chunked(且未被代理提前转为 content-length) - 响应体末尾在
0\r\n后,紧跟着类似X-Request-Duration: 124ms\r\nX-Signature: abc123\r\n\r\n的 trailer 块
可用 curl -v 或 Wireshark 抓包验证原始响应流,注意查看 0\r\n 之后是否真有键值对行。若没有,则 $sent_trailer_* 始终为空,日志中不会出现对应字段。
在 Nginx 配置中启用并引用 $sent_trailer_*
Nginx 默认会接收并解析合法 trailer,但需确保:
- 使用的是支持 trailer 的版本(Nginx ≥ 1.13.2,且未禁用
underscores_in_headers off影响字段名) - 在
log_format中直接引用变量,例如:log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$sent_trailer_X_Request_Duration" "$sent_trailer_X_Signature"'; - 注意变量名转换规则:HTTP 头部
X-Request-Duration对应变量名为$sent_trailer_X_Request_Duration(连字符转下划线,首字母大写保持)
日志中处理缺失或空值的健壮性
并非每次响应都有 trailer,变量可能为空字符串。为避免日志中出现大量短横线或空格,建议:
- 用
map提供默认值:map $sent_trailer_X_Request_Duration $duration_log { default $sent_trailer_X_Request_Duration; "" "-"; }
然后在 log_format 中用$duration_log - 若 trailer 值含空格或特殊字符(如 JSON),Nginx 默认不转义,日志解析器需能处理;必要时可用
escape=json(需 Nginx ≥ 1.19.6 + ngx_http_log_module 支持) - 避免在
access_log中直接拼接未校验的 trailer 变量到敏感字段(如 user_id),防止注入风险
调试与验证方法
快速验证 trailer 是否被正确捕获:
- 临时添加
add_header X-Debug-Trailer "$sent_trailer_X_Request_Duration";到 location 块,用 curl 查看响应头 - 开启 Nginx debug 日志(
error_log /path/to/debug.log debug;),搜索trailer关键字,确认解析逻辑是否触发 - 若始终为空,检查上游服务是否真正在 chunked body 结束后发送 trailer(常见错误:后端写了
Trailer头但没发实际 trailer 行,或用了 gzip 压缩导致 trailer 被吞掉)











