从 $request_body 记录 POST 数据

我有我的配置设置来处理一堆 GET 请求,这些请求呈现像素,可以很好地处理分析和解析查询字符串以进行日志记录。对于额外的第三方数据流,我需要处理对给定 URL 的 POST 请求,该 URL 在其请求体内具有预期的可日志格式的 JSON。我不想使用带有 proxy_pass的辅助服务器,只想将整个响应记录到一个相关的日志文件中,就像它对 GET 请求所做的那样。我使用的一些代码片段如下所示:

GET 请求(非常有效) :

location ^~ /rl.gif {
set $rl_lcid $arg_lcid;
if ($http_cookie ~* "lcid=(.*\S)")
{
set $rl_lcid $cookie_lcid;
}
empty_gif;
log_format my_tracking '{ "guid" : "$rl_lcid", "data" : "$arg__rlcdnsegs" }';
access_log  /mnt/logs/nginx/my.access.log my_tracking;
rewrite ^(.*)$ http://my/url?id=$cookie_lcid? redirect;
}

这就是我想要做的: POST 请求(不工作) :

location /bk {
log_format bk_tracking $request_body;
access_log  /mnt/logs/nginx/bk.access.log bk_tracking;
}

冰壶 curl http://myurl/bk -d name=example给我一个404页没有找到。

然后我试着:

location /bk.gif {
empty_gif;
log_format bk_tracking $request_body;
access_log  /mnt/logs/nginx/bk.access.log bk_tracking;
}

冰壶给了我一个 405 Not Allowed

我目前的版本是 nginx/0.7.62。任何帮助在正确的方向是非常感谢! 谢谢!

更新 现在我的帖子是这样的:

location /bk {
if ($request_method != POST) {
return 405;
}
proxy_pass $scheme://127.0.0.1:$server_port/dummy;
log_format my_tracking $request_body;
access_log  /mnt/logs/nginx/my.access.log my_tracking;
}
location /dummy { set $test 0; }

它正确地记录发布数据,但是在请求者端返回一个404。如果我改变上面的代码,返回一个200,如下所示:

location /bk {
if ($request_method != POST) {
return 405;
}
proxy_pass $scheme://127.0.0.1:$server_port/dummy;
log_format my_tracking $request_body;
access_log  /mnt/logs/nginx/my.access.log my_tracking;
return 200;
}
location /dummy { set $test 0; }

然后正确返回 200,但不再记录发布数据。

另一个更新 找到了一个有效的解决方案,希望这能帮到其他人。

234507 次浏览

Ok. So finally I was able to log the post data and return a 200. It's kind of a hacky solution that I'm not too proud of which basically overrides the natural behavior for error_page, but my inexperience of nginx plus timelines lead me to this solution:

location /bk {
if ($request_method != POST) {
return 405;
}
proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
proxy_pass $scheme://127.0.0.1:$server_port/success;
log_format my_tracking $request_body;
access_log  /mnt/logs/nginx/my_tracking.access.log my_tracking;
}
location /success {
return 200;
}
error_page   500 502 503 504  /50x.html;
location = /50x.html {
root   /var/www/nginx-default;
log_format my_tracking $request_body;
access_log  /mnt/logs/nginx/my_tracking.access.log my_tracking_2;
}

Now according to that config, it would seem that the proxy pass would return a 200 all the time. Occasionally I would get 500 but when I threw in an error_log to see what was going on, all of my request_body data was in there and I couldn't see a problem. So I caught that and wrote to the same log. Since nginx doesn't like the same name for the tracking variable, I just used my_tracking_2 and wrote to the same log as when it returns a 200. Definitely not the most elegant solution and I welcome any better solution. I've seen the post module, but in my scenario, I couldn't recompile from source.

This solution works like a charm:

http {


log_format postdata $request_body;


server {
location = /post.php {
access_log  /var/log/nginx/postdata.log  postdata;
fastcgi_pass php_cgi;
}
}
}

I think the trick is making nginx believe that you will call a CGI script.

Edit 2022-03-15: there is some discussion on where log_format should be set. The documentation clearly says that it needs to be in the http context: http://nginx.org/en/docs/http/ngx_http_log_module.html#log_format

If you put log_format in the server context, nginx will fail to load the config: nginx: [emerg] "log_format" directive is not allowed here in <path>:<line> (tested with nginx 1.20 on ubuntu 18.04)

I had a similar problem. GET requests worked and their (empty) request bodies got written to the the log file. POST requests failed with a 404. Experimenting a bit, I found that all POST requests were failing. I found a forum posting asking about POST requests and the solution there worked for me. That solution? Add a proxy_header line right before the proxy_pass line, exactly like the one in the example below.

server {
listen       192.168.0.1:45080;
server_name  foo.example.org;


access_log  /path/to/log/nginx/post_bodies.log post_bodies;
location / {
### add the following proxy_header line to get POSTs to work
proxy_set_header Host $http_host;
proxy_pass   http://10.1.2.3;
}
}

(This is with nginx 1.2.1 for what it is worth.)

Try echo_read_request_body.

"echo_read_request_body ... Explicitly reads request body so that the $request_body variable will always have non-empty values (unless the body is so big that it has been saved by Nginx to a local temporary file)."

location /log {
log_format postdata $request_body;
access_log /mnt/logs/nginx/my_tracking.access.log postdata;
echo_read_request_body;
}

FWIW, this config worked for me:

location = /logpush.html {
if ($request_method = POST) {
access_log /var/log/nginx/push.log push_requests;
proxy_pass $scheme://127.0.0.1/logsink;
break;
}
return 200 $scheme://$host/serviceup.html;
}
#
location /logsink {
return 200;
}

nginx log format taken from here: http://nginx.org/en/docs/http/ngx_http_log_module.html

no need to install anything extra

worked for me for GET and POST requests:

upstream my_upstream {
server upstream_ip:upstream_port;
}


location / {
log_format postdata '$remote_addr - $remote_user [$time_local] '
'"$request" $status $bytes_sent '
'"$http_referer" "$http_user_agent" "$request_body"';
access_log /path/to/nginx_access.log postdata;
proxy_set_header Host $http_host;
proxy_pass http://my_upstream;
}
}

just change upstream_ip and upstream_port

The solution below was the best format I found.

log_format postdata escape=json '$remote_addr - $remote_user [$time_local] '
'"$request" $status $bytes_sent '
'"$http_referer" "$http_user_agent" "$request_body"';
server {
listen 80;


server_name api.some.com;


location / {
access_log  /var/log/nginx/postdata.log  postdata;
proxy_pass      http://127.0.0.1:8080;
}


}

For this input

curl -d '{"key1":"value1", "key2":"value2"}' -H "Content-Type: application/json" -X POST http://api.deprod.com/postEndpoint

Generate that great result

201.23.89.149 -  [22/Aug/2019:15:58:40 +0000] "POST /postEndpoint HTTP/1.1" 200 265 "" "curl/7.64.0" "{\"key1\":\"value1\", \"key2\":\"value2\"}"

I think the correct answer is below exhibit:

location /bk {
if ($request_method != POST) {
return 405;
}
proxy_pass $scheme://127.0.0.1:$server_port/dummy;
log_format my_tracking $request_body;
access_log  /mnt/logs/nginx/my.access.log my_tracking;
  

}
location /dummy {
access_log off;
return 200;
}