How to Configure ProxyPreserveHost on Individual ProxyPass Rules in Apache


2 views

When implementing reverse proxy configurations in Apache, many developers encounter scenarios where they need different ProxyPreserveHost behaviors for different backend targets. The standard ProxyPreserveHost directive applies globally, but sometimes we need more granular control.

Consider these common situations:


# For Varnish cache (needs original host header)
ProxyPass /cache http://localhost:6081
ProxyPassReverse /cache http://localhost:6081

# For external API (needs rewritten host header)  
ProxyPass /api https://thirdparty.example.com
ProxyPassReverse /api https://thirdparty.example.com

Since ProxyPreserveHost can't be set per-proxy, we use RequestHeader directives:


<Location "/cache">
    ProxyPass http://localhost:6081
    ProxyPassReverse http://localhost:6081
    RequestHeader set Host "%{HTTP_HOST}e"
</Location>

<Location "/api">
    ProxyPass https://thirdparty.example.com
    ProxyPassReverse https://thirdparty.example.com
    # No Host header manipulation - uses backend host
</Location>

For more complex scenarios:


# Conditional header setting
<Location "/dynamic">
    ProxyPass http://backend.example.com
    RequestHeader set Host "%{HTTP_HOST}e" env=PRESERVE_HOST
    RequestHeader unset Host env=!PRESERVE_HOST
</Location>

Verify your configuration with:


curl -v http://yourserver/cache/some-path
curl -v http://yourserver/api/endpoint

Check the Host header in your backend access logs to confirm proper behavior.

While this solution works, be aware that header manipulation adds minor overhead. For high-traffic sites, consider:

  • Keeping the most frequent paths in the default configuration
  • Using separate virtual hosts when possible
  • Monitoring request processing times

When configuring Apache's reverse proxy with multiple backend services, we often encounter situations where different ProxyPass rules require different ProxyPreserveHost settings. The specific case involves:

  • A local Varnish instance that needs original Host header preservation
  • Third-party services that require Host header modification

By default, ProxyPreserveHost is a server-wide directive that affects all ProxyPass rules uniformly. This becomes problematic when you need:


# This affects ALL ProxyPass rules
ProxyPreserveHost On

We can leverage Apache's <Location> blocks to apply different ProxyPreserveHost settings:


<VirtualHost *:80>
    ServerName example.com
    
    # Default setting for most rules
    ProxyPreserveHost Off
    
    # Special case for Varnish
    <Location /cached-content/>
        ProxyPreserveHost On
        ProxyPass http://localhost:6081/
        ProxyPassReverse http://localhost:6081/
    </Location>
    
    # Third-party service
    <Location /external-api/>
        ProxyPreserveHost Off
        ProxyPass http://thirdparty.example.com/
        ProxyPassReverse http://thirdparty.example.com/
    </Location>
</VirtualHost>

For more complex scenarios, we can use environment variables to control the behavior:


SetEnvIf Request_URI "^/cached-content/" PRESERVE_HOST=1
    
<IfDefine PRESERVE_HOST>
    ProxyPreserveHost On
</IfDefine>
    
ProxyPass /cached-content/ http://localhost:6081/
ProxyPass /external-api/ http://thirdparty.example.com/
  • Always include matching ProxyPassReverse directives
  • Test with curl -v to verify Host header behavior
  • Clear proxy cache when changing these settings
  • Consider using Header set Host "custom-value" for more control

Here's a production-tested configuration:


<VirtualHost *:443>
    SSLEngine on
    # SSL certificates here...
    
    # Default for all routes
    ProxyPreserveHost Off
    
    # Varnish cache layer
    <LocationMatch "^/(products|images)/">
        ProxyPreserveHost On
        ProxyPass http://127.0.0.1:6081/ timeout=300 retry=0
        ProxyPassReverse http://127.0.0.1:6081/
    </LocationMatch>
    
    # External payment gateway
    <Location /checkout/payment>
        ProxyPreserveHost Off
        RequestHeader set Host "api.paymentprovider.com"
        ProxyPass https://api.paymentprovider.com/v2/
        ProxyPassReverse https://api.paymentprovider.com/v2/
    </Location>
</VirtualHost>