When dealing with HTTP POST requests, the standard Nginx rewrite rule using 301/302 redirects will cause the POST data to be lost. This happens because:
rewrite ^/(.*)$ http://newdomain.com/$1 permanent;
The browser converts POST to GET during the redirect - this is standard HTTP behavior. What we need is a true proxy pass that maintains the original request method and body.
Instead of rewrite, use Nginx's proxy module to forward the complete request:
location / {
proxy_pass http://newdomain.com;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
For more complex scenarios with HTTPS and custom headers:
location /api/ {
proxy_pass https://api.newdomain.com/;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Content-Type $content_type;
proxy_set_header Authorization $http_authorization;
proxy_pass_request_body on;
proxy_pass_request_headers on;
}
To maintain the original request characteristics:
location / {
proxy_method $request_method;
proxy_pass_request_headers on;
proxy_pass_request_body on;
proxy_pass http://backend;
proxy_set_header X-Original-URI $request_uri;
}
When troubleshooting POST data forwarding:
- Check Nginx error logs:
tail -f /var/log/nginx/error.log
- Verify headers with
curl -v -X POST -d "test=data" http://yourdomain.com
- Use tcpdump to inspect raw traffic:
tcpdump -i any port 80 -A
For high-traffic POST endpoints, consider adding:
proxy_buffers 16 32k;
proxy_buffer_size 64k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
client_max_body_size 10M;
When attempting to forward POST requests between domains using Nginx's rewrite
directive with the permanent
flag, you'll encounter an important limitation:
rewrite ^/(.*)$ http://mydomain/$1 permanent;
This approach converts POST requests to GET requests during the redirect, causing the request body to be lost. This happens because:
- HTTP 301/302 redirects typically don't preserve the request method
- The client browser or application initiates a new GET request after the redirect
To properly forward POST requests with their payload intact, we need to use Nginx's proxy_pass
directive instead of rewrite
:
server {
listen 80;
server_name domainA.com;
location / {
proxy_pass http://domainB.com;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Optional: Handle large POST bodies
client_max_body_size 100M;
proxy_request_buffering off;
}
}
The critical components in this solution:
proxy_pass
: Forwards requests to the target domain while preserving the original HTTP method- Header settings: Ensure proper request handling and tracing
client_max_body_size
: Adjust for large file uploadsproxy_request_buffering
: Can be disabled for better performance with large requests
If you must use redirects (not proxy passing), consider this approach for POST requests:
map $request_method $redirect_method {
default permanent;
POST redirect;
}
server {
# ...
rewrite ^/(.*)$ http://domainB.com/$1 $redirect_method;
}
While this still converts POST to GET, it at least makes the behavior explicit and allows different handling for different HTTP methods.
Use curl to verify POST data preservation:
curl -X POST http://domainA.com/api -d '{"test":"data"}' -H "Content-Type: application/json"
Check your domainB.com access logs to confirm the POST data arrives intact.
When using proxy_pass:
- Enable keepalive connections to upstream
- Consider adding proxy buffers for better throughput
- Monitor connection metrics between servers
proxy_http_version 1.1;
proxy_set_header Connection "";
keepalive_timeout 75s;
keepalive_requests 1000;