| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> |
| <HTML><HEAD> |
| <TITLE>Dynamically configured mass virtual hosting</TITLE> |
| </HEAD> |
| |
| <!-- Background white, links blue (unvisited), navy (visited), red (active) --> |
| <BODY |
| BGCOLOR="#FFFFFF" |
| TEXT="#000000" |
| LINK="#0000FF" |
| VLINK="#000080" |
| ALINK="#FF0000" |
| > |
| <!--#include virtual="header.html" --> |
| <H1 ALIGN="CENTER">Dynamically configured mass virtual hosting</H1> |
| |
| <P>This document describes how to efficiently serve an arbitrary number |
| of virtual hosts with Apache 1.3. |
| |
| <!-- |
| |
| Written by Tony Finch (fanf@demon.net) (dot@dotat.at). |
| |
| Some examples were derived from Ralf S. Engleschall's document |
| http://www.engelschall.com/pw/apache/rewriteguide/ |
| |
| Some suggestions were made by Brian Behlendorf. |
| |
| --> |
| |
| <H2><A NAME="contents">Contents:</A></H2> |
| |
| <UL> |
| <LI><A HREF="#motivation">Motivation</A> |
| <LI><A HREF="#overview">Overview</A> |
| <LI><A HREF="#simple">Simple dynamic virtual hosts</A> |
| <LI><A HREF="#homepages">A virtually hosted homepages system</A> |
| <LI><A HREF="#combinations">Using more than one virtual hosting system on the same server</A> |
| <LI><A HREF="#ipbased">More efficient IP-based virtual hosting</A> |
| <LI><A HREF="#oldversion">Using older versions of Apache</A> |
| <LI><A HREF="#simple.rewrite">Simple dynamic virtual hosts using <CODE>mod_rewrite</CODE></A> |
| <LI><A HREF="#homepages.rewrite">A homepages system using <CODE>mod_rewrite</CODE></A> |
| <LI><A HREF="#xtra-conf">Using a separate virtual host configuration file</A> |
| </UL> |
| |
| <HR><H2><A NAME="motivation">Motivation</A></H2> |
| |
| <P>The techniques described here are of interest if your |
| <CODE>httpd.conf</CODE> contains many |
| <CODE><VirtualHost></CODE> sections that are substantially the |
| same, for example: |
| <PRE> |
| NameVirtualHost 111.22.33.44 |
| <VirtualHost 111.22.33.44> |
| ServerName www.customer-1.com |
| DocumentRoot /www/hosts/www.customer-1.com/docs |
| ScriptAlias /cgi-bin/ /www/hosts/www.customer-1.com/cgi-bin |
| </VirtualHost> |
| <VirtualHost 111.22.33.44> |
| ServerName www.customer-2.com |
| DocumentRoot /www/hosts/www.customer-2.com/docs |
| ScriptAlias /cgi-bin/ /www/hosts/www.customer-2.com/cgi-bin |
| </VirtualHost> |
| # blah blah blah |
| <VirtualHost 111.22.33.44> |
| ServerName www.customer-N.com |
| DocumentRoot /www/hosts/www.customer-N.com/docs |
| ScriptAlias /cgi-bin/ /www/hosts/www.customer-N.com/cgi-bin |
| </VirtualHost> |
| </PRE> |
| </P> |
| |
| <P>The basic idea is to replace all of the static |
| <CODE><VirtualHost></CODE> configuration with a mechanism that |
| works it out dynamically. This has a number of advantages: |
| <OL> |
| <LI>Your configuration file is smaller so Apache starts faster and |
| uses less memory. |
| <LI>Adding virtual hosts is simply a matter of creating the |
| appropriate directories in the filesystem and entries in the DNS - |
| you don't need to reconfigure or restart Apache. |
| </OL> |
| </P> |
| |
| <P>The main disadvantage is that you cannot have a different log file |
| for each virtual host; however if you have very many virtual hosts |
| then doing this is dubious anyway because it eats file descriptors. It |
| is better to log to a pipe or a fifo and arrange for the process at |
| the other end to distribute the logs to the customers (it can also |
| accumulate statistics, etc.).</P> |
| |
| |
| <HR><H2><A NAME="overview">Overview</A></H2> |
| |
| <P>A virtual host is defined by two pieces of information: its IP |
| address, and the contents of the <CODE>Host:</CODE> header in the HTTP |
| request. The dynamic mass virtual hosting technique is based on |
| automatically inserting this information into the pathname of the file |
| that is used to satisfy the request. This is done most easily using |
| <A HREF="../mod/mod_vhost_alias.html"><CODE>mod_vhost_alias</CODE></A>, |
| but if you are using a version of Apache up to 1.3.6 then you must use |
| <A HREF="../mod/mod_rewrite.html"><CODE>mod_rewrite</CODE></A>. Both |
| of these modules are disabled by default; you must enable one of them |
| when configuring and building Apache if you want to use this technique.</P> |
| |
| <P>A couple of things need to be `faked' to make the dynamic virtual |
| host look like a normal one. The most important is the server name |
| which is used by Apache to generate self-referential URLs, etc. It |
| is configured with the <CODE>ServerName</CODE> directive, and it is |
| available to CGIs via the <CODE>SERVER_NAME</CODE> environment |
| variable. The actual value used at run time is controlled by the |
| <A HREF="../mod/core.html#usecanonicalname"><CODE>UseCanonicalName</CODE></A> |
| setting. With <CODE>UseCanonicalName Off</CODE> the server name |
| comes from the contents of the <CODE>Host:</CODE> header in the |
| request. With <CODE>UseCanonicalName DNS</CODE> it comes from a |
| reverse DNS lookup of the virtual host's IP address. The former |
| setting is used for name-based dynamic virtual hosting, and the latter |
| is used for IP-based hosting. If Apache cannot work out the server |
| name because there is no <CODE>Host:</CODE> header or the DNS lookup |
| fails then the value configured with <CODE>ServerName</CODE> is used |
| instead.</P> |
| |
| <P>The other thing to `fake' is the document root (configured |
| with <CODE>DocumentRoot</CODE> and available to CGIs via the |
| <CODE>DOCUMENT_ROOT</CODE> environment variable). This setting |
| is used by the core module when mapping URIs to filenames, but |
| when the server is configured to do dynamic virtual hosting that |
| job is taken over by another module. If any CGIs or SSI documents |
| make use of the <CODE>DOCUMENT_ROOT</CODE> environment variable |
| they will therefore get a misleading value; there isn't any way to |
| change <CODE>DOCUMENT_ROOT</CODE> dynamically.</P> |
| |
| |
| <HR><H2><A NAME="simple">Simple dynamic virtual hosts</A></H2> |
| |
| <P>This extract from <CODE>httpd.conf</CODE> implements the virtual |
| host arrangement outlined in the <A HREF="#motivation">Motivation</A> |
| section above, but in a generic fashion using |
| <CODE>mod_vhost_alias</CODE>.</P> |
| |
| <PRE> |
| # get the server name from the Host: header |
| UseCanonicalName Off |
| |
| # this log format can be split per-virtual-host based on the first field |
| LogFormat "%V %h %l %u %t \"%r\" %s %b" vcommon |
| CustomLog logs/access_log vcommon |
| |
| # include the server name in the filenames used to satisfy requests |
| VirtualDocumentRoot /www/hosts/%0/docs |
| VirtualScriptAlias /www/hosts/%0/cgi-bin |
| </PRE> |
| |
| <P>This configuration can be changed into an IP-based virtual hosting |
| solution by just turning <CODE>UseCanonicalName Off</CODE> into |
| <CODE>UseCanonicalName DNS</CODE>. The server name that is inserted |
| into the filename is then derived from the IP address of the virtual |
| host.</P> |
| |
| |
| <HR><H2><A NAME="homepages">A virtually hosted homepages system</A></H2> |
| |
| <P>This is an adjustment of the above system tailored for an ISP's |
| homepages server. Using a slightly more complicated configuration we |
| can select substrings of the server name to use in the filename so |
| that e.g. the documents for <SAMP>www.user.isp.com</SAMP> are found in |
| <CODE>/home/user/</CODE>. It uses a single <CODE>cgi-bin</CODE> |
| directory instead of one per virtual host.</P> |
| |
| <PRE> |
| # all the preliminary stuff is the same as above, then |
| |
| # include part of the server name in the filenames |
| VirtualDocumentRoot /www/hosts/%2/docs |
| |
| # single cgi-bin directory |
| ScriptAlias /cgi-bin/ /www/std-cgi/ |
| </PRE> |
| |
| <P>There are examples of more complicated |
| <CODE>VirtualDocumentRoot</CODE> settings in |
| <A HREF="../mod/mod_vhost_alias.html">the |
| <CODE>mod_vhost_alias</CODE> documentation</A>.</P> |
| |
| |
| <HR><H2><A NAME="combinations">Using more than one virtual hosting |
| system on the same server</A></H2> |
| |
| <P>With more complicated setups you can use Apache's normal |
| <CODE><VirtualHost></CODE> directives to control the scope of |
| the various virtual hosting configurations. For example, you could |
| have one IP address for homepages customers and another for commercial |
| customers with the following setup. This can of course be combined |
| with conventional <CODE><VirtualHost></CODE> configuration |
| sections.</P> |
| |
| <PRE> |
| UseCanonicalName Off |
| |
| LogFormat "%V %h %l %u %t \"%r\" %s %b" vcommon |
| |
| <Directory /www/commercial> |
| Options FollowSymLinks |
| AllowOverride All |
| </Directory> |
| |
| <Directory /www/homepages> |
| Options FollowSymLinks |
| AllowOverride None |
| </Directory> |
| |
| <VirtualHost 111.22.33.44> |
| ServerName www.commercial.isp.com |
| |
| CustomLog logs/access_log.commercial vcommon |
| |
| VirtualDocumentRoot /www/commercial/%0/docs |
| VirtualScriptAlias /www/commercial/%0/cgi-bin |
| </VirtualHost> |
| |
| <VirtualHost 111.22.33.45> |
| ServerName www.homepages.isp.com |
| |
| CustomLog logs/access_log.homepages vcommon |
| |
| VirtualDocumentRoot /www/homepages/%0/docs |
| ScriptAlias /cgi-bin/ /www/std-cgi/ |
| </VirtualHost> |
| </PRE> |
| |
| |
| <HR><H2><A NAME="ipbased">More efficient IP-based virtual hosting</A></H2> |
| |
| <P>After <A HREF="#simple">the first example</A> I noted that it is |
| easy to turn it into an IP-based virtual hosting setup. Unfortunately |
| that configuration is not very efficient because it requires a DNS |
| lookup for every request. This can be avoided by laying out the |
| filesystem according to the IP addresses themselves rather than the |
| corresponding names and changing the logging similarly. Apache will |
| then usually not need to work out the server name and so incur a DNS |
| lookup.</P> |
| |
| <PRE> |
| # get the server name from the reverse DNS of the IP address |
| UseCanonicalName DNS |
| |
| # include the IP address in the logs so they may be split |
| LogFormat "%A %h %l %u %t \"%r\" %s %b" vcommon |
| CustomLog logs/access_log vcommon |
| |
| # include the IP address in the filenames |
| VirtualDocumentRootIP /www/hosts/%0/docs |
| VirtualScriptAliasIP /www/hosts/%0/cgi-bin |
| </PRE> |
| |
| |
| <HR><H2><A NAME="oldversion">Using older versions of Apache</A></H2> |
| |
| <P>The examples above rely on <CODE>mod_vhost_alias</CODE> which |
| appeared after version 1.3.6. If you are using a version of Apache |
| without <CODE>mod_vhost_alias</CODE> then you can implement this |
| technique with <CODE>mod_rewrite</CODE> as illustrated below, but |
| only for Host:-header-based virtual hosts.</P> |
| |
| <P>In addition there are some things to beware of with logging. Apache |
| 1.3.6 is the first version to include the <CODE>%V</CODE> log format |
| directive; in versions 1.3.0 - 1.3.3 the <CODE>%v</CODE> option did |
| what <CODE>%V</CODE> does; version 1.3.4 has no equivalent. In |
| all these versions of Apache the <CODE>UseCanonicalName</CODE> |
| directive can appear in <CODE>.htaccess</CODE> files which means that |
| customers can cause the wrong thing to be logged. Therefore the best |
| thing to do is use the <CODE>%{Host}i</CODE> directive which logs the |
| <CODE>Host:</CODE> header directly; note that this may include |
| <CODE>:port</CODE> on the end which is not the case for |
| <CODE>%V</CODE>.</P> |
| |
| |
| <HR><H2><A NAME="simple.rewrite">Simple dynamic virtual hosts |
| using <CODE>mod_rewrite</CODE></A></H2> |
| |
| <P>This extract from <CODE>httpd.conf</CODE> does the same thing as |
| <A HREF="#simple">the first example</A>. The first half is very |
| similar to the corresponding part above but with some changes for |
| backward compatibility and to make the <CODE>mod_rewrite</CODE> part |
| work properly; the second half configures <CODE>mod_rewrite</CODE> to |
| do the actual work.</P> |
| |
| <P>There are a couple of especially tricky bits: By default, |
| <CODE>mod_rewrite</CODE> runs before the other URI translation modules |
| (<CODE>mod_alias</CODE> etc.) so if they are used then |
| <CODE>mod_rewrite</CODE> must be configured to accommodate them. |
| Also, mome magic must be performed to do a per-dynamic-virtual-host |
| equivalent of <CODE>ScriptAlias</CODE>.</P> |
| |
| <PRE> |
| # get the server name from the Host: header |
| UseCanonicalName Off |
| |
| # splittable logs |
| LogFormat "%{Host}i %h %l %u %t \"%r\" %s %b" vcommon |
| CustomLog logs/access_log vcommon |
| |
| <Directory /www/hosts> |
| # ExecCGI is needed here because we can't force |
| # CGI execution in the way that ScriptAlias does |
| Options FollowSymLinks ExecCGI |
| </Directory> |
| |
| # now for the hard bit |
| |
| RewriteEngine On |
| |
| # a ServerName derived from a Host: header may be any case at all |
| RewriteMap lowercase int:tolower |
| |
| ## deal with normal documents first: |
| # allow Alias /icons/ to work - repeat for other aliases |
| RewriteCond %{REQUEST_URI} !^/icons/ |
| # allow CGIs to work |
| RewriteCond %{REQUEST_URI} !^/cgi-bin/ |
| # do the magic |
| RewriteRule ^/(.*)$ /www/hosts/${lowercase:%{SERVER_NAME}}/docs/$1 |
| |
| ## and now deal with CGIs - we have to force a MIME type |
| RewriteCond %{REQUEST_URI} ^/cgi-bin/ |
| RewriteRule ^/(.*)$ /www/hosts/${lowercase:%{SERVER_NAME}}/cgi-bin/$1 [T=application/x-httpd-cgi] |
| |
| # that's it! |
| </PRE> |
| |
| |
| <HR><H2><A NAME="homepages.rewrite">A homepages system |
| using <CODE>mod_rewrite</CODE></A></H2> |
| |
| <P>This does the same thing as <A HREF="#homepages">the |
| second example</A>.</P> |
| |
| <PRE> |
| RewriteEngine on |
| |
| RewriteMap lowercase int:tolower |
| |
| # allow CGIs to work |
| RewriteCond %{REQUEST_URI} !^/cgi-bin/ |
| |
| # check the hostname is right so that the RewriteRule works |
| RewriteCond ${lowercase:%{SERVER_NAME}} ^www\.[a-z-]+\.isp\.com$ |
| |
| # concatenate the virtual host name onto the start of the URI |
| # the [C] means do the next rewrite on the result of this one |
| RewriteRule ^(.+) ${lowercase:%{SERVER_NAME}}$1 [C] |
| |
| # now create the real file name |
| RewriteRule ^www\.([a-z-]+)\.isp\.com/(.*) /home/$1/$2 |
| |
| # define the global CGI directory |
| ScriptAlias /cgi-bin/ /www/std-cgi/ |
| </PRE> |
| |
| |
| <HR><H2><A NAME="xtra-conf">Using a separate virtual host configuration file</A></H2> |
| |
| <P>This arrangement uses more advanced <CODE>mod_rewrite</CODE> |
| features to get the translation from virtual host to document root |
| from a separate configuration file. This provides more flexibility but |
| requires more complicated configuration.</P> |
| |
| <P>The <CODE>vhost.map</CODE> file contains something like this: |
| <PRE> |
| www.customer-1.com /www/customers/1 |
| www.customer-2.com /www/customers/2 |
| # ... |
| www.customer-N.com /www/customers/N |
| </PRE> |
| </P> |
| |
| <P>The <CODE>http.conf</CODE> contains this: |
| <PRE> |
| RewriteEngine on |
| |
| RewriteMap lowercase int:tolower |
| |
| # define the map file |
| RewriteMap vhost txt:/www/conf/vhost.map |
| |
| # deal with aliases as above |
| RewriteCond %{REQUEST_URI} !^/icons/ |
| RewriteCond %{REQUEST_URI} !^/cgi-bin/ |
| RewriteCond ${lowercase:%{SERVER_NAME}} ^(.+)$ |
| # this does the file-based remap |
| RewriteCond ${vhost:%1} ^(/.*)$ |
| RewriteRule ^/(.*)$ %1/docs/$1 |
| |
| RewriteCond %{REQUEST_URI} ^/cgi-bin/ |
| RewriteCond ${lowercase:%{SERVER_NAME}} ^(.+)$ |
| RewriteCond ${vhost:%1} ^(/.*)$ |
| RewriteRule ^/(.*)$ %1/cgi-bin/$1 |
| </PRE> |
| </P> |
| |
| <!--#include virtual="footer.html" --> |
| </BODY> |
| </HTML> |