When running Apache behind a load balancer (like AWS ELB), you often need to handle SSL termination at the balancer level while enforcing HTTPS at the web server level. The standard approach of checking SERVER_PORT
won't work because the connection from the load balancer always comes in on port 80.
The core issue is that mod_rewrite
has limited access to HTTP headers. While you might expect to use %{HTTP_X_FORWARDED_PORT}
, this syntax only works with certain predefined headers. Here's what won't work:
RewriteCond %{HTTP_X_FORWARDED_PORT} !=443 # This fails
Solution 1: Using mod_setenvif
First, we'll capture the header value into an environment variable:
<VirtualHost *:80>
ServerName www.example.com
# Capture X-Forwarded-Port header
SetEnvIf X-Forwarded-Port (.*) XFPORT=$1
RewriteEngine On
RewriteCond %{ENV:XFPORT} !=443
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
# Rest of your configuration
</VirtualHost>
Solution 2: Using mod_headers
If you have mod_headers
available, you can set the header as an environment variable:
<VirtualHost *:80>
ServerName www.example.com
# Set environment variable from header
RequestHeader set XFPORT expr=%{REQUEST_HEADERS:X-Forwarded-Port}
RewriteEngine On
RewriteCond %{ENV:XFPORT} !=443
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
</VirtualHost>
In cases where the header might be missing, add an additional condition:
RewriteCond %{ENV:XFPORT} !^443$ [OR]
RewriteCond %{ENV:XFPORT} ^$
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
For AWS environments, you might also want to check the X-Forwarded-Proto
header:
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
When running Apache behind a load balancer (like AWS ELB), we often need to handle port-based redirects differently than in standard configurations. The main obstacle is that mod_rewrite
doesn't directly support checking arbitrary HTTP headers like X-Forwarded-Port
through the typical %{HTTP_HEADER}
syntax.
Here's the most reliable approach I've found after testing multiple AWS ELB configurations:
ServerName www.example.com
# First capture the header value into an environment variable
SetEnvIf X-Forwarded-Port (.+) X_FORWARDED_PORT=$1
RewriteEngine On
# Now check the environment variable
RewriteCond %{ENV:X_FORWARDED_PORT} !^443$
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L,QSA]
# Rest of your configuration...
For environments where mod_setenvif
isn't available:
ServerName www.example.com
# Capture header via mod_headers
RequestHeader set X-Forwarded-Port-Env "expr=%{HTTP:X-Forwarded-Port}"
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Port-Env} !^443$
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
When implementing this:
- Always test with
curl -v -H "X-Forwarded-Port: 80" http://yoursite.com
before going live - The ELB must be configured to pass through the
X-Forwarded-Port
header - Consider adding a fallback condition for direct requests:
RewriteCond %{HTTP:X-Forwarded-Port} !^443$ [OR]
RewriteCond %{SERVER_PORT} !^443$
If redirects aren't working as expected:
- Check Apache error logs:
tail -f /var/log/apache2/error.log
- Verify headers are being received:
awk '/X-Forwarded-Port/ {print}' /var/log/apache2/access.log
- Test with
RewriteLogLevel 3
temporarily to debug rewrite rules