nginx ≥1.11.0 原生支持 $request_id,需确认版本、在 location 块配置 proxy_set_header、log_format 中用 "$request_id",并用 map 指令兜底透传已有 id,否则链路追踪中断。

Nginx 的请求 ID 追踪不需要装模块、不依赖 Lua,只要版本 ≥ 1.11.0,$request_id 就能直接用——关键不是“能不能用”,而是“配置错哪一环就断链”。
确认 Nginx 版本是否真支持 $request_id
执行 nginx -v,输出必须是 nginx/1.11.0 或更高(如 nginx/1.22.1)。低于这个版本的 Nginx 会把 $request_id 当作普通字符串处理,日志里显示为空或字面量 $request_id,proxy 透传也无效。
- 别信
nginx -V输出里有没有set_misc模块——$request_id属于ngx_http_core_module,和那些第三方模块无关 - 验证是否生效:在
log_format里加$request_id,重启后查access.log,看到稳定输出 32 位十六进制串(如e9a8b7c6d5f4a3b2c1e0f9d8a7b6c5d4)才算真正可用 - 1.10.x 及更老版本必须升级,没有兼容补丁
proxy_set_header X-Request-ID $request_id 必须写在 location 块内
这行配置不能放在 http 或 server 顶层,否则对 proxy_pass 完全无效。Nginx 的 proxy_set_header 是 location 级指令,只作用于当前块内的转发行为。
- 错误写法:
http { proxy_set_header X-Request-ID $request_id; }→ 后端收不到该 header - 正确写法:
location /api { proxy_set_header X-Request-ID $request_id; proxy_pass http://backend; } - 如果用了多个 upstream(比如
/user和/order),每个location都得单独写一遍,无法继承 - 不要加引号:
proxy_set_header X-Request-ID "$request_id"在部分旧版 Nginx 中会导致变量不展开,留空
log_format 中必须用 $request_id,别用 $http_x_request_id
$http_x_request_id 是从客户端请求头读的,不是 Nginx 自己生成的 ID。前端没带、中间代理清掉了、curl 测试时漏了 -H "X-Request-ID:...",这个变量就是空字符串,日志字段直接塌陷。
- 推荐日志格式:
log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" req_id:"$request_id"'; - 引号很重要:加
"$request_id"而不是$request_id,避免 Logstash/Grok 把十六进制串截断或误识别为多个字段 - 不要混用:
X-Trace-ID或traceparent是 W3C Trace Context 规范字段,需要完整采样逻辑;X-Request-ID是轻量锚点,统一用它,别让后端解析错乱
透传逻辑要兜底:map 指令处理上游已带 ID 的场景
如果流量经过 API 网关、CDN 或其他反向代理,可能已经携带了 X-Request-ID。此时应优先沿用,而不是覆盖,否则链路 ID 在入口就断了。
- 在
http块顶部加:map $http_x_request_id $req_id { default $http_x_request_id; "" $request_id; } - 后续所有地方替换为
$req_id:add_header X-Request-ID $req_id always;、proxy_set_header X-Request-ID $req_id;、日志里也写$req_id - 这个
map是“有则用之,无则生成”的最小成本兜底,比后端自己 fallback 更可靠
真正容易被忽略的是:Nginx 只管注入和透传,$request_id 到达后端后,如果服务没读、没存、没打到日志里,整条链就只剩 Nginx 一侧有记录——排查时依然找不到上下文。











