[logging] Logging POST data from $request_body

I have my config setup to handle a bunch of GET requests which render pixels that work fine to handle analytics and parse query strings for logging. With an additional third party data stream, I need to handle a POST request to a given url that has JSON in an expected loggable format inside of it's request body. I don't want to use a secondary server with proxy_pass and just want to log the whole response into an associated log file like what it does with GET requests. A snippet of some code that I'm using looks like the following:

GET request (which works great):

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;
}

Here is kinda what I am trying to do: POST request (which does not work):

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

Curling curl http://myurl/bk -d name=example gives me a 404 page not found.

Then I tried:

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

Curling curl http://myurl/bk.gif -d name=example gives me a 405 Not Allowed.

My current version is nginx/0.7.62. Any help in the right direction is very much appreciated! Thanks!

UPDATE So now my post looks like this:

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; }

It is logging the post data correctly, but returns a 404 on the requesters end. If I change the above code to return a 200 like so:

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; }

Then it return the 200 correctly, but no longer records the post data.

ANOTHER UPDATE Kinda found a working solution. Hopefully this can help other on their way.

This question is related to logging nginx http-post

The answer is


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.)


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.


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


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;
}

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;
}

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\"}"

Examples related to logging

How to redirect docker container logs to a single file? Console logging for react? Hide strange unwanted Xcode logs Where are logs located? Retrieve last 100 lines logs Spring Boot - How to log all requests and responses with exceptions in single place? How do I get logs from all pods of a Kubernetes replication controller? Where is the Docker daemon log? How to log SQL statements in Spring Boot? How to do logging in React Native?

Examples related to nginx

Kubernetes service external ip pending nginx: [emerg] "server" directive is not allowed here Disable nginx cache for JavaScript files Nginx upstream prematurely closed connection while reading response header from upstream, for large requests Nginx: Job for nginx.service failed because the control process exited How can I have same rule for two locations in NGINX config? How to verify if nginx is running or not? Find nginx version? Docker Networking - nginx: [emerg] host not found in upstream How do I rewrite URLs in a proxy response in NGINX

Examples related to http-post

Passing headers with axios POST request How to post raw body data with curl? Send FormData with other field in AngularJS How do I POST a x-www-form-urlencoded request using Fetch? OkHttp Post Body as JSON What is the difference between PUT, POST and PATCH? HTTP Request in Swift with POST method Uploading file using POST request in Node.js Send POST request with JSON data using Volley AngularJS $http-post - convert binary to excel file and download