When working with Nginx as a reverse proxy, a common requirement is forwarding custom headers to backend servers like Apache. The default Nginx configuration only passes standard headers, dropping any custom ones like X-CUSTOM-REFERRER
unless explicitly configured.
For forwarding specific headers, use proxy_set_header
directives:
location / {
proxy_pass http://apache_backend;
proxy_set_header X-CUSTOM-REFERRER $http_x_custom_referrer;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
For a more flexible solution that forwards all incoming headers:
location / {
proxy_pass http://apache_backend;
proxy_pass_request_headers on;
# Forward all headers that start with 'X-'
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
# Capture all custom headers
more_set_input_headers 'X-: $http_x_';
more_set_headers 'X-: $sent_http_x_';
}
1. The $http_
prefix converts headers to variables (dashes become underscores)
2. For the dynamic solution, you may need the headers-more-nginx-module
3. Always verify headers reach Apache using:
curl -I -H "X-CUSTOM-REFERRER: testvalue" http://yourdomain.com
If headers aren't appearing in Apache:
- Check Nginx error logs for header processing issues
- Verify no other Nginx rules are modifying headers
- Test with tcpdump
between Nginx and Apache
- Add this to Apache config to log all headers:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %{X-CUSTOM-REFERRER}i" combined
When forwarding numerous headers:
- Large headers increase memory usage
- Each proxy_set_header
adds processing overhead
- Consider whitelisting only necessary headers in production
When working with Nginx as a reverse proxy, one common requirement is forwarding custom HTTP headers to backend servers like Apache. Many developers struggle with this because:
- Nginx doesn't forward non-standard headers by default
- The proxy_set_header directive can be tricky to configure properly
- Header names might get normalized (e.g., underscores removed)
For your specific case with X-CUSTOM-REFERRER
, here's how to modify your config:
location / {
proxy_pass http://preview;
proxy_redirect off;
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-Custom-Referrer $http_x_custom_referrer;
}
Key points about this solution:
- Use
$http_
prefix followed by the header name (lowercase, with hyphens replaced by underscores) - The header name in proxy_set_header can be different from the original header name
- Nginx automatically normalizes header names in variables
If you need to forward all incoming headers automatically, you'll need a more advanced solution:
location / {
proxy_pass http://preview;
# Preserve original headers
proxy_pass_request_headers on;
# Forward all headers that start with X- or x-
proxy_set_header X-Forwarded-Proto $scheme;
# Special handling for headers with underscores
underscores_in_headers on;
# Forward specific headers you know about
proxy_set_header X-Custom-Referrer $http_x_custom_referrer;
# Forward arbitrary headers (requires Nginx 1.13.8+)
proxy_set_header "$header_name" "$http_header_name";
}
When header forwarding doesn't work as expected:
- Check for underscores in header names (enable
underscores_in_headers
) - Verify the header reaches Nginx (check access logs)
- Inspect Apache's received headers using:
# Apache config to log headers
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{X-Custom-Referrer}i\"" custom
CustomLog /var/log/apache2/access.log custom
While forwarding all headers might seem convenient, it has implications:
- Increased memory usage for each request
- Potential security risks from forwarding unnecessary headers
- Header size limitations (configure with
proxy_buffer_size
)
For most production environments, explicitly listing required headers offers better security and performance.
Sometimes you need context-aware forwarding. Here's an example using map:
map $http_x_special_header $should_forward {
default 0;
"special-value" 1;
}
server {
location / {
proxy_pass http://backend;
if ($should_forward) {
proxy_set_header X-Special-Header $http_x_special_header;
}
}
}