nginx 默认不记录响应体,需用三种方案实现:一、用$log_format+$body_bytes_sent粗略记录响应大小;二、用lua-nginx-module在body_filter_by_lua_block中截取并落盘响应体;三、前置代理(如go服务)拦截响应做结构化脱敏日志。

要让 Nginx 对特定接口的响应内容(如 JSON 返回体)做实时日志记录,仅靠标准 location 块 + log_format 是不够的——Nginx 默认不记录响应体($response_body 不存在),需借助第三方模块或代理层配合。以下是实用、可落地的几种方案:
方案一:使用 ngx_http_log_module(需编译时启用)+ $body_bytes_sent 粗粒度记录
这是最轻量、无需额外模块的方式,适用于只需记录“是否返回成功数据”或“响应大小变化”的场景:
- 在
http块中定义日志格式,包含响应状态、大小、时间、URI 等关键字段:
log_format api_log '$time_iso8601 | $status | $body_bytes_sent | $request_length | "$request" | "$http_user_agent"';
- 在
location块中匹配目标接口(例如/api/v1/user),并指定该日志格式:
location /api/v1/user {
access_log /var/log/nginx/user_api.log api_log;
proxy_pass http://backend;
}⚠️ 注意:它不记录响应内容本身,但能实时反映接口是否返回了有效载荷(比如 $body_bytes_sent > 0 且 $status == 200),适合监控和初步审计。
方案二:用 lua-nginx-module 实现响应体捕获(推荐)
这是目前最主流、可控性最强的方案,通过 OpenResty 或编译含 lua 模块的 Nginx,在 body_filter_by_lua_block 中读取并记录响应体:
- 确保已安装 OpenResty 或 Nginx + lua-nginx-module;
- 在
http块中启用共享字典(用于暂存响应体,避免大响应阻塞):
lua_shared_dict log_buffer 10m;
- 在对应
location中添加 Lua 处理逻辑:
location /api/v1/order {
access_log /var/log/nginx/order_api_access.log main;
<pre class="brush:php;toolbar:false;"># 记录请求头、参数等(可选)
set $log_data "";
proxy_pass http://order_backend;
# 在响应体生成后捕获(注意:只对非 chunked、非 gzip 响应稳定)
body_filter_by_lua_block {
local resp = string.sub(ngx.arg[1], 1, 2048) -- 截取前 2KB 防爆
if resp and #resp > 0 then
local log_line = string.format("%s | %s | %s | %s\n",
os.date("%Y-%m-%d %H:%M:%S"),
ngx.var.status,
ngx.var.request_uri,
ngx.escape_json(resp)
)
-- 写入本地文件(生产环境建议用 logger 进程或 syslog)
local f = io.open("/var/log/nginx/order_response.log", "a")
if f then
f:write(log_line)
f:close()
end
end
-- 清空缓冲区,避免重复写入
ngx.arg[1] = ""
}}
✅ 优势:可精确控制记录时机、内容长度、脱敏逻辑(如过滤 token 字段);
⚠️ 注意:不要在高并发大响应场景中无截断地记录完整 body,否则影响性能和磁盘 IO。
方案三:前置代理 + 日志旁路(适合生产级审计)
将敏感接口流量导入专用日志代理(如 Envoy、Traefik 或自研 Go 中间件),由其完成响应捕获与结构化落盘:
- Nginx 仅作路由转发,将目标 location 流量 proxy_pass 到本地监听的 Go 服务;
- Go 服务完成:接收请求 → 转发给真实后端 → 拦截响应 → 解析 JSON → 脱敏(如隐藏身份证、手机号)→ 写入 Kafka/ES/文件;
- 示例 Nginx 配置:
location /api/v1/payment {
proxy_pass http://127.0.0.1:8081; # 指向日志代理
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}✅ 优势:解耦清晰、易于扩展日志策略、支持异步写入与告警;
✅ 合规友好:便于实现 GDPR/等保要求的字段级脱敏与留存周期控制。
避坑提醒
- 不要尝试用 $request_body 记录响应:它是请求体变量,与响应无关;
- 避免在 log_format 中直接引用未定义变量(如 $response_body),会导致日志条目为空或报错;
-
gzip 或流式响应(Transfer-Encoding: chunked)下,body_filter_by_lua 可能收不到完整 body,需结合
lua_need_request_body on和响应头判断(如检查 Content-Length); - 日志文件权限与轮转必须配置,否则可能因写满磁盘导致 Nginx 报错或服务中断。










