How to Configure Apache Reverse Proxy with Multiple Backend Virtual Hosts on Debian Servers


5 views

When implementing a frontend-backend server architecture with Apache, the key challenge is ensuring proper host header propagation. Your current setup shows a common pitfall where the backend server isn't correctly identifying virtual hosts despite proper frontend proxy configuration.

The problem stems from missing ProxyPreserveHost directive in your frontend configuration. Without this, the backend server receives requests without the original Host header, causing it to default to the first VirtualHost block.

# Corrected frontend configuration example

    ServerName dev.example.com
    ProxyPreserveHost On
    ProxyPass / http://192.168.144.100:80/
    ProxyPassReverse / http://192.168.144.100:80/

Here's the full working configuration for both servers:

# Frontend server (proxy) - /etc/apache2/sites-available/000-default.conf

    ServerName dev.example.com
    ProxyPreserveHost On
    ProxyPass / http://192.168.144.100:80/
    ProxyPassReverse / http://192.168.144.100:80/
    ErrorLog ${APACHE_LOG_DIR}/dev_error.log
    CustomLog ${APACHE_LOG_DIR}/dev_access.log combined



    ServerName sandbox.example.com
    ProxyPreserveHost On
    ProxyPass / http://192.168.144.100:80/
    ProxyPassReverse / http://192.168.144.100:80/
    ErrorLog ${APACHE_LOG_DIR}/sandbox_error.log
    CustomLog ${APACHE_LOG_DIR}/sandbox_access.log combined

The backend server needs proper NameVirtualHost declaration and should listen for the specific hostnames:

# Backend server - /etc/apache2/sites-available/000-default.conf
NameVirtualHost *:80


    ServerName default.example.com
    DocumentRoot /var/www/html
    # Default catch-all configuration



    ServerName dev.example.com
    DocumentRoot /var/www/example.com/dev
    
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    



    ServerName sandbox.example.com
    DocumentRoot /var/www/example.com/sandbox
    
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    

After making these changes, run these commands:

sudo a2enmod proxy proxy_http
sudo systemctl restart apache2

Verify the Host header propagation using curl:

curl -v -H "Host: dev.example.com" http://frontend-server-ip/

For better performance and security, consider adding:

ProxyRequests Off
ProxyVia Off

    Require all granted


I recently set up a two-server web architecture with:

  • Frontend server handling incoming requests on port 80
  • Backend server receiving proxied requests from the frontend

The challenge emerged when trying to route multiple domains (dev.example.com and sandbox.example.com) to different document roots on the backend server through Apache's reverse proxy.

Here's what my frontend Apache configuration looks like:

<VirtualHost *:80>
    ServerName dev.example.com
    ProxyPass / http://192.168.144.100:80/
    ProxyPassReverse / http://192.168.144.100:80/
</VirtualHost>

<VirtualHost *:80>
    ServerName sandbox.example.com
    ProxyPass / http://192.168.144.100:80/
    ProxyPassReverse / http://192.168.144.100:80/
</VirtualHost>

And the backend configuration:

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/
    <Directory />
        Options FollowSymLinks
        AllowOverride None
    </Directory>
    <Directory /var/www/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        allow from all
    </Directory>
</VirtualHost>

<VirtualHost *:80>
    ServerName dev.example.com
    DocumentRoot /var/www/example.com/dev/
    <Directory /var/www/example.com/dev/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        allow from all
    </Directory>
</VirtualHost>

<VirtualHost *:80>
    ServerName sandbox.example.com
    DocumentRoot /var/www/example.com/sandbox/
    <Directory /var/www/example.com/sandbox/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        allow from all
    </Directory>
</VirtualHost>

The key issue was that the frontend server wasn't passing the original Host header to the backend. Adding ProxyPreserveHost On to the frontend configuration solved this:

<VirtualHost *:80>
    ServerName dev.example.com
    ProxyPreserveHost On
    ProxyPass / http://192.168.144.100:80/
    ProxyPassReverse / http://192.168.144.100:80/
</VirtualHost>

<VirtualHost *:80>
    ServerName sandbox.example.com
    ProxyPreserveHost On
    ProxyPass / http://192.168.144.100:80/
    ProxyPassReverse / http://192.168.144.100:80/
</VirtualHost>

After making these changes and restarting Apache, you can verify the setup with:

curl -I http://dev.example.com
curl -I http://sandbox.example.com

Check that the responses show the correct document roots from the backend server.

For more complex setups, you might want to:

  1. Use ProxyRequests Off for security
  2. Implement SSL termination on the frontend
  3. Set up proper logging for debugging

Here's an enhanced version with these improvements:

<VirtualHost *:80>
    ServerName dev.example.com
    ProxyPreserveHost On
    ProxyRequests Off
    ProxyPass / http://192.168.144.100:80/
    ProxyPassReverse / http://192.168.144.100:80/
    ErrorLog ${APACHE_LOG_DIR}/dev-error.log
    CustomLog ${APACHE_LOG_DIR}/dev-access.log combined
</VirtualHost>