nginx日志请求中request_body 为空分析

一、为什么 $request_body 始终是空的(核心原因) 1️⃣ $request_body 并不是“自动可用变量” 在 Nginx 中: 请求头:在 rewrite / access 阶段就已解析 请求体(body): 只有在“模块明...

一、为什么 $request_body 始终是空的(核心原因)

1️⃣ $request_body 并不是“自动可用变量”

在 Nginx 中:

  • 请求头:在 rewrite / access 阶段就已解析

  • 请求体(body)
    只有在“模块明确读取 body”时才会被加载到内存

而你当前的 location /notify:


return 200 "{\"code\" : 0,\"msg\" : \"接收成功\"}";

属于 rewrite 阶段直接结束请求,此时:

  • Nginx 不会去读 request body

  • body 数据还在 socket 里

  • $request_body 根本还不存在

这是设计行为,不是 bug

2️⃣ access_log 发生在「请求完成之后」,但 body 从未被读过

Nginx 的处理顺序简化如下:


rewrite phase └── return 200(请求直接结束) └── access_log

关键点:

  • return 不会触发 request body 读取

  • access_log 只能记录“已经解析过的变量”

  • $request_body 未被加载 → 为空字符串

3️⃣ 你注释掉的 echo 模块,反而是“正确方向”

你这里:

# echo_read_request_body; # set $req_body $request_body; # echo $request_body;

如果启用 echo_read_request_body,body 就能读出来

这不是巧合,而是因为:

echo_read_request_body 强制触发了请求体读取

二、即使是 POST + JSON,也不会自动读取 body

你用的 curl 请求是完全正确的:

curl --location --request POST https://order-dev.sbmplc.com/notify \ --header 'Content-Type: application/json' \ --data-raw '[ { "test": "Hello" } ]'

这点要强调:

  • ❌ 是否 POST —— 无关

  • ❌ 是否 JSON —— 无关

  • ❌ 是否有 Content-Length —— 无关

  • 是否有模块“消费 request body”—— 决定性因素

三、为什么反向代理时又“有时候能记录到 body”

很多人会疑惑:

“我 proxy_pass 的时候,$request_body 有时是有的?”

原因是:

  • proxy_pass 会读取 body 并转发给 upstream

  • 读取动作发生 → $request_body 被填充

  • access_log 才能记录

而你现在是 纯 return 场景


四、正确可用的解决方案(生产级)

下面是可选方案,从推荐到不推荐

✅ 方案一(推荐):用 ngx_http_lua_module 读取 body

最干净、可控、生产可用

location /notify {

content_by_lua_block {

ngx.req.read_body()

local body = ngx.req.get_body_data()

ngx.log(ngx.INFO, "request body: ", body)

ngx.header["Content-Type"] = "application/json"

ngx.say('{"code":0,"msg":"接收成功"}')

}

}

优点:

  • 明确读取 body

  • 不污染 access_log

  • 可做签名校验、JSON 解析、限流等

  • 支付宝 / 微信 / 回调场景最常用

✅ 方案二:必须记录到 access_log(不推荐,但可行)

强制让 Nginx 读 body:

location /notify {

client_max_body_size 1m;

client_body_buffer_size 1m;

client_body_in_single_buffer on;

proxy_pass http://127.0.0.1:65535; # 黑洞 upstream

}

配一个“假 upstream”,只为触发 body 读取。

缺点:非语义化,增加一次无意义的 proxy,调试成本高

⚠️ 方案三:echo 模块(仅调试用)

location /notify {

echo_read_request_body;

access_log /usr/local/nginx/logs/notify_body.log access;

return 200 '{"code":0,"msg":"接收成功"}'; }

说明:

  • echo 模块不是官方模块

  • 不建议生产环境长期使用

  • 但用于验证问题非常合适

五、为什么「官方文档很少明说」这个坑

因为 Nginx 的设计哲学是:

“不要为你没用到的东西付出性能成本”

请求体可能是:几 KB,几百 MB

chunked stream

所以:默认不读取,只有模块明确需要时才读

六、你的现象总结一句话

你在 location 中使用 return 直接结束请求,
Nginx 从未读取 request body,
因此 $request_body 在 access_log 中永远是空的。

  • 发表于 2026-01-14 12:19
  • 阅读 ( 17 )

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
shitian
shitian

662 篇文章

作家榜 »

  1. shitian 662 文章
  2. 石天 437 文章
  3. 每天惠23 33 文章
  4. 小A 29 文章