When working with Nginx's proxy_pass directive, many developers encounter difficulties logging the complete proxied URI including query parameters. The standard Nginx variables like $uri, $proxy_host, and $upstream_addr don't provide the full picture of what's being passed to the upstream server.
Consider this common proxy configuration:
location ~* ^/?(foo/?.*$) {
proxy_pass https://www.someserver.com/some-thing-else/$1;
break;
}
When a request comes in like:
https://www.originalserver.com/foo?page=5
Current logging variables might only show:
proxy_host: www.someserver.com
upstream_addr: 123.45.67.890:443
uri_path: /foo
uri_query: page=5
To log the complete proxied URI including the query string, we need to modify our log_format. Here's an enhanced version that includes the full proxied URI:
log_format json_enhanced escape=json '{'
'\"time_local\": \"$time_local\",'
'\"core\": {'
'\"site\": \"$server_name\",'
'\"server\": \"$host\",'
'\"full_proxied_uri\": \"$scheme://$proxy_host$uri$is_args$args\",'
'\"dest_port\": \"$server_port\",'
'\"src_ip\": \"$realip_remote_addr\",'
'\"status\": \"$status\",'
'\"protocol\": \"$server_protocol\",'
'\"body_bytes_sent\": \"$body_bytes_sent\",'
'\"remote_addr\": \"$remote_addr\",'
'\"remote_user\": \"$remote_user\",'
'\"request\": \"$request\",'
'\"nginx_version\": \"$nginx_version\",'
'\"http\": {'
'\"http_referer\": \"$http_referer\",'
'\"http_user_agent\": \"$http_user_agent\",'
'\"http_x_header\": \"$http_x_header\",'
'\"uri_query\": \"$query_string\",'
'\"uri_path\": \"$uri\",'
'\"http_method\": \"$request_method\",'
'\"response_time\": \"$upstream_response_time\",'
'\"cookie\": \"$http_cookie\",'
'\"request_time\": \"$request_time\",'
'\"http_x_forwarded_for\": \"$http_x_forwarded_for\",'
'\"proxy_host\": \"$proxy_host\",'
'\"upstream_addr\": \"$upstream_addr\"'
'}'
'}'
'}';
For more complex scenarios, you can create a custom variable to capture the full proxied URI:
location ~* ^/?(foo/?.*$) {
set $full_proxied_uri https://www.someserver.com/some-thing-else/$1$is_args$args;
proxy_pass $full_proxied_uri;
break;
access_log /var/log/nginx/proxy_access.log json_enhanced;
}
If you suspect the query string is being dropped somewhere in the chain, add these debugging headers:
location ~* ^/?(foo/?.*$) {
proxy_pass https://www.someserver.com/some-thing-else/$1;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Proxied-URI $uri$is_args$args;
break;
}
Here's a complete example combining all these techniques:
http {
log_format json_enhanced escape=json '{'
'\"full_proxied_uri\": \"$scheme://$proxy_host$uri$is_args$args\"'
# ... other fields ...
'}';
server {
listen 80;
server_name www.originalserver.com;
location ~* ^/?(foo/?.*$) {
set $full_proxied_uri https://www.someserver.com/some-thing-else/$1$is_args$args;
proxy_pass $full_proxied_uri;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Proxied-URI $uri$is_args$args;
access_log /var/log/nginx/proxy_access.log json_enhanced;
break;
}
}
}
When working with Nginx's proxy_pass directive, many developers encounter difficulties in logging the complete proxied URI including query parameters. The standard logging variables often provide fragmented information that requires manual reconstruction.
With your current configuration:
location ~* ^/?(foo/?.*$) {
proxy_pass https://www.someserver.com/some-thing-else/$1;
break;
}
The request https://www.originalserver.com/foo?page=5
gets transformed to https://www.someserver.com/some-thing-else/foo?page=5
, but your logs only show:
proxy_host: www.someserver.com
upstream_addr: 123.45.67.890:443
uri_path: /foo
uri_query: page=5
To log the complete proxied URI, we need to create a custom variable in the location block:
location ~* ^/?(foo/?.*$) {
set $full_proxy_uri "https://www.someserver.com/some-thing-else/$1$is_args$args";
proxy_pass $full_proxy_uri;
break;
}
Modify your log_format to include the complete URI:
log_format json escape=json '{'
'\"time_local\": \"$time_local\",'
'\"core\": {'
'\"site\": \"$server_name\",'
'\"server\": \"$host\",'
'\"dest_port\": \"$server_port\",'
'\"src_ip\": \"$realip_remote_addr\",'
'\"status\": \"$status\",'
'\"protocol\": \"$server_protocol\",'
'\"body_bytes_sent\": \"$body_bytes_sent\",'
'\"remote_addr\": \"$remote_addr\",'
'\"remote_user\": \"$remote_user\",'
'\"request\": \"$request\",'
'\"nginx_version\": \"$nginx_version\",'
'\"http\": {'
'\"http_referer\": \"$http_referer\",'
'\"http_user_agent\": \"$http_user_agent\",'
'\"http_x_header\": \"$http_x_header\",'
'\"uri_query\": \"$query_string\",'
'\"uri_path\": \"$uri\",'
'\"http_method\": \"$request_method\",'
'\"response_time\": \"$upstream_response_time\",'
'\"cookie\": \"$http_cookie\",'
'\"request_time\": \"$request_time\",'
'\"http_x_forwarded_for\": \"$http_x_forwarded_for\",'
'\"proxy_host\": \"$proxy_host\",'
'\"upstream_addr\": \"$upstream_addr\",'
'\"full_proxied_uri\": \"$full_proxy_uri\"'
'}'
'}'
'}';
For simpler cases, you might use $request_uri which includes both path and query string:
set $proxied_path "/some-thing-else$request_uri";
If query strings are being dropped later in the chain, consider these debugging steps:
# Temporary debug logging
location ~* ^/?(foo/?.*$) {
add_header X-Debug-Proxied-Uri "https://www.someserver.com/some-thing-else/$1$is_args$args";
proxy_pass https://www.someserver.com/some-thing-else/$1$is_args$args;
break;
}
When implementing detailed logging:
- Use conditional logging for production environments
- Consider buffering logs for high-traffic sites
- Monitor disk space usage for verbose logs
Watch out for these issues:
- Missing $is_args between path and query string
- URI encoding problems with special characters
- Upstream servers that modify or strip query parameters