When working with Nginx as a reverse proxy, developers often encounter situations where custom headers from client requests don't make it to the backend servers. This is particularly frustrating when you're trying to pass authentication tokens, special API keys, or other custom metadata between services.
Looking at the example configuration:
location / {
proxy_read_timeout 2000;
proxy_next_upstream error;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass_header X_CUSTOM_HEADER;
proxy_redirect off;
proxy_max_temp_file_size 0;
proxy_pass https://prod;
break;
}
The key issue here is using proxy_pass_header
which only works for response headers from the backend, not request headers from the client.
There are two correct approaches to forward custom headers:
Method 1: Using proxy_set_header
location / {
# ... other directives ...
proxy_set_header X_CUSTOM_HEADER $http_x_custom_header;
proxy_pass https://prod;
}
The $http_
prefix is crucial here - Nginx automatically converts incoming headers to variables with this prefix and lowercase names.
Method 2: Wildcard forwarding
location / {
# ... other directives ...
proxy_pass_request_headers on;
proxy_pass https://prod;
}
This approach forwards all headers, but might be less secure in some scenarios.
To verify headers are being forwarded:
curl -v -H "X_CUSTOM_HEADER: testvalue" http://yourdomain.com
Check both Nginx access logs and backend server logs to trace the header flow.
- Header name case sensitivity (Nginx normalizes to lowercase)
- Underscores in header names (may require
underscores_in_headers on;
) - Header size limitations (
large_client_header_buffers
)
http {
underscores_in_headers on;
large_client_header_buffers 4 32k;
server {
location / {
proxy_set_header X-Custom-Header $http_x_custom_header;
proxy_set_header X-Another-Header $http_x_another_header;
proxy_pass http://backend;
}
}
}
This comprehensive configuration handles most edge cases while maintaining security.
When setting up Nginx as a reverse proxy, you might encounter situations where custom headers sent by clients aren't being passed through to your backend servers (like Apache/PHP). This behavior occurs because Nginx, by default, only forwards a specific set of headers to backend services.
# Problematic Nginx configuration example
location / {
proxy_pass https://prod;
proxy_set_header Host $http_host;
# Missing custom header forwarding
}
To properly forward custom headers, you need to understand these key directives:
# Solution configuration
location / {
proxy_pass https://prod;
# Standard headers
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
# Custom header handling
proxy_pass_header X_CUSTOM_HEADER;
proxy_set_header X_CUSTOM_HEADER $http_x_custom_header;
# Other important settings
proxy_redirect off;
proxy_read_timeout 2000;
}
The original configuration used proxy_pass_header
alone, which isn't sufficient. You need both:
proxy_pass_header
- Allows passing specific headers from Nginx to clientproxy_set_header
- Forwards headers from client to backend
Here's a fully functional configuration that handles various header scenarios:
server {
listen 80;
server_name domain.com;
location /api/ {
proxy_pass https://backend_server;
# Standard headers
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
# Custom headers
proxy_pass_header X_CUSTOM_HEADER;
proxy_set_header X_CUSTOM_HEADER $http_x_custom_header;
# Additional headers example
proxy_set_header X-API-Version $http_x_api_version;
proxy_pass_header X-API-Secret;
# Performance settings
proxy_read_timeout 300;
proxy_connect_timeout 300;
}
}
Verify your setup with these curl commands:
# Test custom header forwarding
curl -v -H "X_CUSTOM_HEADER: test_value" http://domain.com/api/endpoint
# Test multiple headers
curl -v \
-H "X_CUSTOM_HEADER: foo" \
-H "X-API-Version: 2.1" \
http://domain.com/api/test
For more complex scenarios, consider these approaches:
# Forward all headers (not recommended for production)
location / {
proxy_pass http://backend;
proxy_pass_request_headers on;
}
# Conditional header forwarding
map $http_x_custom_header $forward_custom_header {
default $http_x_custom_header;
"" "";
}
location / {
proxy_set_header X_CUSTOM_HEADER $forward_custom_header;
}
- Header name case sensitivity (Nginx normalizes headers to lowercase)
- Underscores in header names may require
underscores_in_headers on
- Headers exceeding buffer sizes need
proxy_buffer_size
adjustment - HTTPS backends may require additional proxy_set_header directives