How to Conditionally Rewrite URLs Based on Custom HTTP Headers in Apache (X-Forwarded-Port Example)


7 views

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:

  1. Check Apache error logs: tail -f /var/log/apache2/error.log
  2. Verify headers are being received: awk '/X-Forwarded-Port/ {print}' /var/log/apache2/access.log
  3. Test with RewriteLogLevel 3 temporarily to debug rewrite rules