12.10 Frontend/Backend Proxying with Virtual Hosts

This section explains a configuration setup for proxying your backend mod_perl servers when you need to use virtual hosts.

12.10.1 Virtual Host Flavors

Apache supports three flavors of virtual hosts:

IP-based virtual hosts

In this form, each virtual host uses its own IP address. Under Unix, multiple IP addresses are assigned to the same network interface using the ifconfig utility. These additional IP addresses are sometimes called virtual addresses or IP aliases. IP-based virtual hosting is the oldest form of virtual hosting. Due to the supposed increasing scarcity of IP addresses and ensuing difficulty in obtaining large network blocks in some parts of the world, IP-based virtual hosting is now less preferred than name-based virtual hosting.

Name-based virtual hosts

Name-based virtual hosts share a single IP address. Apache dispatches requests to the appropriate virtual host by examining the Host: HTTP header field. This field's value is the hostname extracted from the requested URI. Although this header is mandatory for HTTP 1.1 clients, it has also been widely used by HTTP 1.0 clients for many years.

Port-based virtual hosts

In this setup, all virtual hosts share the same IP address, but each uses its own unique port number. As we'll discuss in the next section, port-based virtual hosts are mostly useful for backend servers not directly accessible from Internet clients.

Mixed flavors

It is perfectly possible to mix the various virtual host flavors in one server.

12.10.2 Dual-Server Virtual Host Configuration

In the dual-server setup, which virtual host flavor is used on the frontend (reverse proxy) server is irrelevant. When running a large number of virtual hosts, it is generally preferable to use name-based virtual hosts, since they share a single IP address. HTTP clients have been supporting this since 1995.

SSL-enabled sites cannot use this scheme, however. This is because when using SSL, all HTTP traffic is encrypted, and this includes the request's Host: header. This header is unavailable until the SSL handshake has been performed, and that in turn requires that the request has been dispatched to the appropriate virtual host, because the SSL handshake depends on that particular host's SSL certificate. For this reason, each SSL-enabled virtual host needs its own, unique IP address. You can still use name-based virtual hosts along with SSL-enabled virtual hosts in the same configuration file, though.

For the backend mod_perl-enabled server, we recommend using port-based virtual hosts using the IP address 127.0.0.1 (localhost). This enforces the fact that this server is accessible only from the frontend server and not directly by clients.

12.10.3 Virtual Hosts and Main Server Interaction

When using virtual hosts, any configuration directive outside of a <VirtualHost> container is applied to a virtual host called the main server, which plays a special role. First, it acts as the default host when you're using name-based virtual hosts and a request can't be mapped to any of the configured virtual hosts (for example, if no Host: header is provided). Secondly, many directives specified for the main server are merged with directives provided in <VirtualHost> containers. In other words, virtual hosts inherit properties from the main server. This allows us to specify default behaviors that will apply to all virtual hosts, while still allowing us to override these behaviors for specific virtual hosts.

In the following example, we use the PerlSetupEnv directive to turn off environment population for all virtual hosts, except for the www.example.com virtual host, which needs it for its legacy CGI scripts running under Apache::Registry:

PerlSetupEnv Off

Listen 8001
<VirtualHost 127.0.0.1:8001>
    ServerName www.example.com
    PerlSetupEnv On
</VirtualHost>

12.10.4 Frontend Server Configuration

The following example illustrates the use of name-based virtual hosts. We define two virtual hosts, www.example.com and www.example.org, which will reverse-proxy dynamic requests to ports 8001 and 8002 on the backend mod_perl-enabled server.

Listen            192.168.1.2:80
NameVirtualHost   192.168.1.2:80

Replace 192.168.1.2 with your server's public IP address.

LogFormat "%v %h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\""

The log format used is the Common Log Format prefixed with %v, a token representing the name of the virtual host. Using a combined log common to all virtual hosts uses fewer system resources. The log file can later be split into seperate files according to the prefix, using splitlog or an equivalent program.

The following are global options for mod_rewrite shared by all virtual hosts:

RewriteLogLevel       0
RewriteRule \.(gif|jpg|png|txt|html)$ - [last]

This turns off the mod_rewrite module's logging feature and makes sure that the frontend server will handle files with the extensions .gif, .jpg, .png, .txt, and .html internally.

If your server is configured to run traditional CGI scripts (under mod_cgi) as well as mod_perl CGI programs, it would be beneficial to configure the frontend server to run the traditional CGI scripts directly. This can be done by altering the (gif|jpg|png|txt|html) rewrite rule to add cgi if all your mod_cgi scripts have the .cgi extension, or by adding a new rule to handle all /cgi-bin/* locations internally.

The virtual hosts setup is straightforward:

##### www.example.com
<VirtualHost 192.168.1.2:80>
    ServerName      www.example.com
    ServerAdmin     webmaster@example.com
    DocumentRoot    /home/httpd_docs/htdocs/www.example.com

    RewriteEngine       on
    RewriteOptions      'inherit'
    RewriteRule         ^/(perl/.*)$     http://127.0.0.1:8001/$1    [P,L]
    ProxyPassReverse    /  http://www.example.com/
</VirtualHost>

##### www.example.org
<VirtualHost 192.168.1.2:80>
    ServerName      www.example.org
    ServerAdmin     webmaster@example.org
    DocumentRoot    /home/httpd_docs/htdocs/www.example.org

    RewriteEngine       on
    RewriteOptions      'inherit'
    RewriteRule         ^/(perl/.*)$     http://127.0.0.1:8002/$1    [P,L]
    ProxyPassReverse    /  http://www.example.org/
</VirtualHost>

The two virtual hosts' setups differ in the DocumentRoot and ProxyPassReverse settings and in the backend ports to which they rewrite.

12.10.5 Backend Server Configuration

This section describes the configuration of the backend server.

The backend server listens on the loopback (localhost) interface:

BindAddress           127.0.0.1

In this context, the following directive does not specify a listening port:

Port                  80

Rather, it indicates which port the server should advertise when issuing a redirect.

The following global mod_perl settings are shared by all virtual hosts:

##### mod_perl settings
PerlRequire                   /home/httpd/perl/startup.pl
PerlFixupHandler              Apache::SizeLimit
PerlPostReadRequestHandler    Book::ProxyRemoteAddr
PerlSetupEnv                  Off

As explained earlier, we use the Book::ProxyRemoteAddr handler to get the real remote IP addresses from the proxy.

We can then proceed to configure the virtual hosts themselves:

##### www.example.com
Listen 8001
<VirtualHost 127.0.0.1:8001>

The Listen directive specifies the port to listen on. A connection to that port will be matched by this <VirtualHost> container.

The remaining configuration is straightforward:

ServerName  www.example.com
ServerAdmin webmaster@example.com

<Location /perl>
    SetHandler perl-script
    PerlHandler Apache::Registry
    Options +ExecCGI
</Location>

<Location /perl-status>
    SetHandler perl-script
    PerlHandler Apache::Status
</Location>

  </VirtualHost>

We configure the second virtual host in a similar way:

##### www.example.org
Listen 8002
<VirtualHost 127.0.0.1:8002>
    ServerName  www.example.org
    ServerAdmin webmaster@example.org

    <Location /perl>
        SetHandler perl-script
        PerlHandler Apache::Registry
        Options +ExecCGI
    </Location>

</VirtualHost>

You may need to specify the DocumentRoot setting in each virtual host if there is any need for it.



    Part I: mod_perl Administration
    Part II: mod_perl Performance
    Part VI: Appendixes