When configuring Apache as a load balancer, many developers need to set the X-Forwarded-Proto
header to correctly pass protocol information to backend servers. A common approach is using:
RequestHeader set X-Forwarded-Proto "%{SERVER_PROTOCOL}e"
However, this often results in the header being set to null
rather than the expected protocol (http/https).
The SERVER_PROTOCOL
environment variable contains the full protocol string (e.g., "HTTP/1.1"), not just the protocol scheme. When Apache tries to use this with RequestHeader
, it may fail to properly interpret the value.
Here are several reliable methods to set the X-Forwarded-Proto
header:
1. Using HTTPS Environment Variable
RequestHeader set X-Forwarded-Proto "https" env=HTTPS
RequestHeader set X-Forwarded-Proto "http" env=!HTTPS
2. Using mod_rewrite
RewriteEngine On
RewriteCond %{HTTPS} =on
RewriteRule .* - [E=PROTO:https]
RewriteCond %{HTTPS} !=on
RewriteRule .* - [E=PROTO:http]
RequestHeader set X-Forwarded-Proto "%{PROTO}e"
3. Using SetEnvIf
SetEnvIf HTTPS "on" PROTO=https
SetEnvIf HTTPS "off" PROTO=http
RequestHeader set X-Forwarded-Proto "%{PROTO}e"
After making changes, test with:
curl -I http://yourdomain.com
curl -I https://yourdomain.com
Look for the X-Forwarded-Proto
header in the response.
- Ensure mod_headers is enabled:
a2enmod headers
- For SSL termination, place these directives in your virtual host configuration
- Clear your browser cache when testing
When configuring Apache as a load balancer, many developers encounter an issue where:
RequestHeader set X-Forwarded-Proto "%{SERVER_PROTOCOL}e"
unexpectedly sets the header value to null
instead of the expected protocol (http/https). This occurs because SERVER_PROTOCOL
isn't available in the early request phase when mod_headers processes the RequestHeader directive.
Apache processes RequestHeader directives very early in the request lifecycle, before many server variables are populated. The environment variable expansion (%{VAR}e
) syntax works, but only for variables that exist at this early stage.
Here's what's happening in the processing timeline:
- Request arrives at Apache
- mod_headers processes RequestHeader directives
- Other modules populate server variables
- Request gets forwarded to backend
Here are three reliable approaches to set X-Forwarded-Proto correctly:
Option 1: Use mod_setenvif with HTTPS
SetEnvIf X-Forwarded-Proto ^https$ HAVE_SSL=yes
RequestHeader set X-Forwarded-Proto "https" env=HAVE_SSL
RequestHeader set X-Forwarded-Proto "http" env=!HAVE_SSL
Option 2: Use mod_rewrite (most reliable)
RewriteEngine On
RewriteCond %{HTTPS} =on
RewriteRule .* - [E=PROTO:https]
RewriteCond %{HTTPS} !=on
RewriteRule .* - [E=PROTO:http]
RequestHeader set X-Forwarded-Proto "%{PROTO}e"
Option 3: Direct HTTPS check
RequestHeader set X-Forwarded-Proto "https" env=HTTPS
RequestHeader set X-Forwarded-Proto "http" env=!HTTPS
After making changes, verify with:
curl -I http://yourdomain.com
curl -Ik https://yourdomain.com
Look for the X-Forwarded-Proto header in responses. For more detailed debugging, enable Apache's log level:
LogLevel debug
The mod_rewrite solution adds minimal overhead as it only sets environment variables. All solutions are more efficient than using mod_php or other late-stage processors to set headers.
- Not enabling required modules (
a2enmod headers rewrite setenvif
) - Placing directives in wrong configuration sections (should be in VirtualHost or server config)
- Forgetting to restart Apache after changes
For complex load balancing setups, consider adding these additional headers:
RequestHeader set X-Forwarded-Port "%{SERVER_PORT}e"
RequestHeader set X-Forwarded-Host "%{HTTP_HOST}e"