| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> |
| <HTML><HEAD> |
| <TITLE>Apache 1.3 URL Rewriting Guide</TITLE> |
| </HEAD> |
| |
| <!-- Background white, links blue (unvisited), navy (visited), red (active) --> |
| <BODY |
| BGCOLOR="#FFFFFF" |
| TEXT="#000000" |
| LINK="#0000FF" |
| VLINK="#000080" |
| ALINK="#FF0000" |
| > |
| <BLOCKQUOTE> |
| <!--#include virtual="header.html" --> |
| |
| <DIV ALIGN=CENTER> |
| |
| <H1> |
| Apache 1.3<BR> |
| URL Rewriting Guide<BR> |
| </H1> |
| |
| <ADDRESS>Originally written by<BR> |
| Ralf S. Engelschall <rse@apache.org><BR> |
| December 1997</ADDRESS> |
| |
| </DIV> |
| |
| <P> |
| This document supplements the mod_rewrite <a |
| href="../mod/mod_rewrite.html">reference documentation</a>. It describes |
| how one can use Apache's mod_rewrite to solve typical URL-based problems |
| webmasters are usually confronted with in practice. I give detailed |
| descriptions on how to solve each problem by configuring URL rewriting |
| rulesets. |
| |
| <H2><a name="ToC1">Introduction to mod_rewrite</a></H2> |
| |
| The Apache module mod_rewrite is a killer one, i.e. it is a really |
| sophisticated module which provides a powerful way to do URL manipulations. |
| With it you can nearly do all types of URL manipulations you ever dreamed |
| about. The price you have to pay is to accept complexity, because |
| mod_rewrite's major drawback is that it is not easy to understand and use for |
| the beginner. And even Apache experts sometimes discover new aspects where |
| mod_rewrite can help. |
| <P> |
| In other words: With mod_rewrite you either shoot yourself in the foot the |
| first time and never use it again or love it for the rest of your life because |
| of its power. This paper tries to give you a few initial success events to |
| avoid the first case by presenting already invented solutions to you. |
| |
| <H2><a name="ToC2">Practical Solutions</a></H2> |
| |
| Here come a lot of practical solutions I've either invented myself or |
| collected from other peoples solutions in the past. Feel free to learn the |
| black magic of URL rewriting from these examples. |
| |
| <P> |
| ATTENTION: Depending on your server-configuration it can be necessary to |
| slightly change the examples for your situation, e.g. adding the [PT] flag |
| when additionally using mod_alias and mod_userdir, etc. Or rewriting a ruleset |
| to fit in <tt>.htaccess</tt> context instead of per-server context. Always try |
| to understand what a particular ruleset really does before you use it. It |
| avoid problems. |
| |
| <H1>URL Layout</H1> |
| |
| <P> |
| <H2>Canonical URLs</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| On some webservers there are more than one URL for a resource. Usually there |
| are canonical URLs (which should be actually used and distributed) and those |
| which are just shortcuts, internal ones, etc. Independed which URL the user |
| supplied with the request he should finally see the canonical one only. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We do an external HTTP redirect for all non-canonical URLs to fix them in the |
| location view of the Browser and for all subsequent requests. In the example |
| ruleset below we replace <tt>/~user</tt> by the canonical <tt>/u/user</tt> and |
| fix a missing trailing slash for <tt>/u/user</tt>. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteRule ^/<b>~</b>([^/]+)/?(.*) /<b>u</b>/$1/$2 [<b>R</b>] |
| RewriteRule ^/([uge])/(<b>[^/]+</b>)$ /$1/$2<b>/</b> [<b>R</b>] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Canonical Hostnames</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| ... |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC] |
| RewriteCond %{HTTP_HOST} !^$ |
| RewriteCond %{SERVER_PORT} !^80$ |
| RewriteRule ^/(.*) http://fully.qualified.domain.name:%{SERVER_PORT}/$1 [L,R] |
| RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC] |
| RewriteCond %{HTTP_HOST} !^$ |
| RewriteRule ^/(.*) http://fully.qualified.domain.name/$1 [L,R] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Moved DocumentRoot</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Usually the DocumentRoot of the webserver directly relates to the URL |
| ``<tt>/</tt>''. But often this data is not really of top-level priority, it is |
| perhaps just one entity of a lot of data pools. For instance at our Intranet |
| sites there are <tt>/e/www/</tt> (the homepage for WWW), <tt>/e/sww/</tt> (the |
| homepage for the Intranet) etc. Now because the data of the DocumentRoot stays |
| at <tt>/e/www/</tt> we had to make sure that all inlined images and other |
| stuff inside this data pool work for subsequent requests. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We just redirect the URL <tt>/</tt> to <tt>/e/www/</tt>. While is seems |
| trivial it is actually trivial with mod_rewrite, only. Because the typical |
| old mechanisms of URL <i>Aliases</i> (as provides by mod_alias and friends) |
| only used <i>prefix</i> matching. With this you cannot do such a redirection |
| because the DocumentRoot is a prefix of all URLs. With mod_rewrite it is |
| really trivial: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteRule <b>^/$</b> /e/www/ [<b>R</b>] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Trailing Slash Problem</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Every webmaster can sing a song about the problem of the trailing slash on |
| URLs referencing directories. If they are missing, the server dumps an error, |
| because if you say <tt>/~quux/foo</tt> instead of |
| <tt>/~quux/foo/</tt> then the server searches for a <i>file</i> named |
| <tt>foo</tt>. And because this file is a directory it complains. Actually |
| is tries to fix it themself in most of the cases, but sometimes this mechanism |
| need to be emulated by you. For instance after you have done a lot of |
| complicated URL rewritings to CGI scripts etc. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| The solution to this subtle problem is to let the server add the trailing |
| slash automatically. To do this correctly we have to use an external redirect, |
| so the browser correctly requests subsequent images etc. If we only did a |
| internal rewrite, this would only work for the directory page, but would go |
| wrong when any images are included into this page with relative URLs, because |
| the browser would request an in-lined object. For instance, a request for |
| <tt>image.gif</tt> in <tt>/~quux/foo/index.html</tt> would become |
| <tt>/~quux/image.gif</tt> without the external redirect! |
| <P> |
| So, to do this trick we write: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteBase /~quux/ |
| RewriteRule ^foo<b>$</b> foo<b>/</b> [<b>R</b>] |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| The crazy and lazy can even do the following in the top-level |
| <tt>.htaccess</tt> file of their homedir. But notice that this creates some |
| processing overhead. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteBase /~quux/ |
| RewriteCond %{REQUEST_FILENAME} <b>-d</b> |
| RewriteRule ^(.+<b>[^/]</b>)$ $1<b>/</b> [R] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Webcluster through Homogeneous URL Layout</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| We want to create a homogenous and consistent URL layout over all WWW servers |
| on a Intranet webcluster, i.e. all URLs (per definition server local and thus |
| server dependent!) become actually server <i>independed</i>! What we want is |
| to give the WWW namespace a consistent server-independend layout: no URL |
| should have to include any physically correct target server. The cluster |
| itself should drive us automatically to the physical target host. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| First, the knowledge of the target servers come from (distributed) external |
| maps which contain information where our users, groups and entities stay. |
| The have the form |
| |
| <P><PRE> |
| user1 server_of_user1 |
| user2 server_of_user2 |
| : : |
| </PRE><P> |
| |
| We put them into files <tt>map.xxx-to-host</tt>. Second we need to instruct |
| all servers to redirect URLs of the forms |
| |
| <P><PRE> |
| /u/user/anypath |
| /g/group/anypath |
| /e/entity/anypath |
| </PRE><P> |
| |
| to |
| |
| <P><PRE> |
| http://physical-host/u/user/anypath |
| http://physical-host/g/group/anypath |
| http://physical-host/e/entity/anypath |
| </PRE><P> |
| |
| when the URL is not locally valid to a server. The following ruleset does |
| this for us by the help of the map files (assuming that server0 is a default |
| server which will be used if a user has no entry in the map): |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| |
| RewriteMap user-to-host txt:/path/to/map.user-to-host |
| RewriteMap group-to-host txt:/path/to/map.group-to-host |
| RewriteMap entity-to-host txt:/path/to/map.entity-to-host |
| |
| RewriteRule ^/u/<b>([^/]+)</b>/?(.*) http://<b>${user-to-host:$1|server0}</b>/u/$1/$2 |
| RewriteRule ^/g/<b>([^/]+)</b>/?(.*) http://<b>${group-to-host:$1|server0}</b>/g/$1/$2 |
| RewriteRule ^/e/<b>([^/]+)</b>/?(.*) http://<b>${entity-to-host:$1|server0}</b>/e/$1/$2 |
| |
| RewriteRule ^/([uge])/([^/]+)/?$ /$1/$2/.www/ |
| RewriteRule ^/([uge])/([^/]+)/([^.]+.+) /$1/$2/.www/$3\ |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Move Homedirs to Different Webserver</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| A lot of webmaster aksed for a solution to the following situation: They |
| wanted to redirect just all homedirs on a webserver to another webserver. |
| They usually need such things when establishing a newer webserver which will |
| replace the old one over time. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| The solution is trivial with mod_rewrite. On the old webserver we just |
| redirect all <tt>/~user/anypath</tt> URLs to |
| <tt>http://newserver/~user/anypath</tt>. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteRule ^/~(.+) http://<b>newserver</b>/~$1 [R,L] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Structured Homedirs</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Some sites with thousend of users usually use a structured homedir layout, |
| i.e. each homedir is in a subdirectory which begins for instance with the |
| first character of the username. So, <tt>/~foo/anypath</tt> is |
| <tt>/home/<b>f</b>/foo/.www/anypath</tt> while <tt>/~bar/anypath</tt> is |
| <tt>/home/<b>b</b>/bar/.www/anypath</tt>. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We use the following ruleset to expand the tilde URLs into exactly the above |
| layout. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteRule ^/~(<b>([a-z])</b>[a-z0-9]+)(.*) /home/<b>$2</b>/$1/.www$3 |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Filesystem Reorganisation</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| This really is a hardcore example: a killer application which heavily uses |
| per-directory <tt>RewriteRules</tt> to get a smooth look and feel on the Web |
| while its data structure is never touched or adjusted. |
| |
| Background: <b><i>net.sw</i></b> is my archive of freely available Unix |
| software packages, which I started to collect in 1992. It is both my hobby and |
| job to to this, because while I'm studying computer science I have also worked |
| for many years as a system and network administrator in my spare time. Every |
| week I need some sort of software so I created a deep hierarchy of |
| directories where I stored the packages: |
| |
| <P><PRE> |
| drwxrwxr-x 2 netsw users 512 Aug 3 18:39 Audio/ |
| drwxrwxr-x 2 netsw users 512 Jul 9 14:37 Benchmark/ |
| drwxrwxr-x 12 netsw users 512 Jul 9 00:34 Crypto/ |
| drwxrwxr-x 5 netsw users 512 Jul 9 00:41 Database/ |
| drwxrwxr-x 4 netsw users 512 Jul 30 19:25 Dicts/ |
| drwxrwxr-x 10 netsw users 512 Jul 9 01:54 Graphic/ |
| drwxrwxr-x 5 netsw users 512 Jul 9 01:58 Hackers/ |
| drwxrwxr-x 8 netsw users 512 Jul 9 03:19 InfoSys/ |
| drwxrwxr-x 3 netsw users 512 Jul 9 03:21 Math/ |
| drwxrwxr-x 3 netsw users 512 Jul 9 03:24 Misc/ |
| drwxrwxr-x 9 netsw users 512 Aug 1 16:33 Network/ |
| drwxrwxr-x 2 netsw users 512 Jul 9 05:53 Office/ |
| drwxrwxr-x 7 netsw users 512 Jul 9 09:24 SoftEng/ |
| drwxrwxr-x 7 netsw users 512 Jul 9 12:17 System/ |
| drwxrwxr-x 12 netsw users 512 Aug 3 20:15 Typesetting/ |
| drwxrwxr-x 10 netsw users 512 Jul 9 14:08 X11/ |
| </PRE><P> |
| |
| In July 1996 I decided to make this 350 MB archive public to the world via a |
| nice Web interface (<a href="http://net.sw.engelschall.com/net.sw/"><tt> |
| http://net.sw.engelschall.com/net.sw/</tt></a>). "Nice" means that I wanted to |
| offer a interface where you can browse directly through the archive hierarchy. |
| And "nice" means that I didn't wanted to change anything inside this hierarchy |
| - not even by putting some CGI scripts at the top of it. Why? Because the |
| above structure should be later accessible via FTP as well, and I didn't |
| want any Web or CGI stuuf to be there. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| The solution has two parts: The first is a set of CGI scripts which create all |
| the pages at all directory levels on-the-fly. I put them under |
| <tt>/e/netsw/.www/</tt> as follows: |
| |
| <P><PRE> |
| -rw-r--r-- 1 netsw users 1318 Aug 1 18:10 .wwwacl |
| drwxr-xr-x 18 netsw users 512 Aug 5 15:51 DATA/ |
| -rw-rw-rw- 1 netsw users 372982 Aug 5 16:35 LOGFILE |
| -rw-r--r-- 1 netsw users 659 Aug 4 09:27 TODO |
| -rw-r--r-- 1 netsw users 5697 Aug 1 18:01 netsw-about.html |
| -rwxr-xr-x 1 netsw users 579 Aug 2 10:33 netsw-access.pl |
| -rwxr-xr-x 1 netsw users 1532 Aug 1 17:35 netsw-changes.cgi |
| -rwxr-xr-x 1 netsw users 2866 Aug 5 14:49 netsw-home.cgi |
| drwxr-xr-x 2 netsw users 512 Jul 8 23:47 netsw-img/ |
| -rwxr-xr-x 1 netsw users 24050 Aug 5 15:49 netsw-lsdir.cgi |
| -rwxr-xr-x 1 netsw users 1589 Aug 3 18:43 netsw-search.cgi |
| -rwxr-xr-x 1 netsw users 1885 Aug 1 17:41 netsw-tree.cgi |
| -rw-r--r-- 1 netsw users 234 Jul 30 16:35 netsw-unlimit.lst |
| </PRE><P> |
| |
| The <tt>DATA/</tt> subdirectory holds the above directory structure, i.e. the |
| real <b><i>net.sw</i></b> stuff and gets automatically updated via |
| <tt>rdist</tt> from time to time. |
| |
| The second part of the problem remains: how to link these two structures |
| together into one smooth-looking URL tree? We want to hide the <tt>DATA/</tt> |
| directory from the user while running the appropriate CGI scripts for the |
| various URLs. |
| |
| Here is the solution: first I put the following into the per-directory |
| configuration file in the Document Root of the server to rewrite the announced |
| URL <tt>/net.sw/</tt> to the internal path <tt>/e/netsw</tt>: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteRule ^net.sw$ net.sw/ [R] |
| RewriteRule ^net.sw/(.*)$ e/netsw/$1 |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| The first rule is for requests which miss the trailing slash! The second rule |
| does the real thing. And then comes the killer configuration which stays in |
| the per-directory config file <tt>/e/netsw/.www/.wwwacl</tt>: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| Options ExecCGI FollowSymLinks Includes MultiViews |
| |
| RewriteEngine on |
| |
| # we are reached via /net.sw/ prefix |
| RewriteBase /net.sw/ |
| |
| # first we rewrite the root dir to |
| # the handling cgi script |
| RewriteRule ^$ netsw-home.cgi [L] |
| RewriteRule ^index\.html$ netsw-home.cgi [L] |
| |
| # strip out the subdirs when |
| # the browser requests us from perdir pages |
| RewriteRule ^.+/(netsw-[^/]+/.+)$ $1 [L] |
| |
| # and now break the rewriting for local files |
| RewriteRule ^netsw-home\.cgi.* - [L] |
| RewriteRule ^netsw-changes\.cgi.* - [L] |
| RewriteRule ^netsw-search\.cgi.* - [L] |
| RewriteRule ^netsw-tree\.cgi$ - [L] |
| RewriteRule ^netsw-about\.html$ - [L] |
| RewriteRule ^netsw-img/.*$ - [L] |
| |
| # anything else is a subdir which gets handled |
| # by another cgi script |
| RewriteRule !^netsw-lsdir\.cgi.* - [C] |
| RewriteRule (.*) netsw-lsdir.cgi/$1 |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| Some hints for interpretation: |
| <ol> |
| <li> Notice the L (last) flag and no substitution field ('-') in the |
| forth part |
| <li> Notice the ! (not) character and the C (chain) flag |
| at the first rule in the last part |
| <li> Notice the catch-all pattern in the last rule |
| </ol> |
| |
| </DL> |
| |
| <P> |
| <H2>NCSA imagemap to Apache mod_imap</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| When switching from the NCSA webserver to the more modern Apache webserver a |
| lot of people want a smooth transition. So they want pages which use their old |
| NCSA <tt>imagemap</tt> program to work under Apache with the modern |
| <tt>mod_imap</tt>. The problem is that there are a lot of |
| hyperlinks around which reference the <tt>imagemap</tt> program via |
| <tt>/cgi-bin/imagemap/path/to/page.map</tt>. Under Apache this |
| has to read just <tt>/path/to/page.map</tt>. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We use a global rule to remove the prefix on-the-fly for all requests: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteRule ^/cgi-bin/imagemap(.*) $1 [PT] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Search pages in more than one directory</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Sometimes it is neccessary to let the webserver search for pages in more than |
| one directory. Here MultiViews or other techniques cannot help. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We program a explicit ruleset which searches for the files in the directories. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| |
| # first try to find it in custom/... |
| # ...and if found stop and be happy: |
| RewriteCond /your/docroot/<b>dir1</b>/%{REQUEST_FILENAME} -f |
| RewriteRule ^(.+) /your/docroot/<b>dir1</b>/$1 [L] |
| |
| # second try to find it in pub/... |
| # ...and if found stop and be happy: |
| RewriteCond /your/docroot/<b>dir2</b>/%{REQUEST_FILENAME} -f |
| RewriteRule ^(.+) /your/docroot/<b>dir2</b>/$1 [L] |
| |
| # else go on for other Alias or ScriptAlias directives, |
| # etc. |
| RewriteRule ^(.+) - [PT] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Set Environment Variables According To URL Parts</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Perhaps you want to keep status information between requests and use the URL |
| to encode it. But you don't want to use a CGI wrapper for all pages just to |
| strip out this information. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We use a rewrite rule to strip out the status information and remember it via |
| an environment variable which can be later dereferenced from within XSSI or |
| CGI. This way a URL <tt>/foo/S=java/bar/</tt> gets translated to |
| <tt>/foo/bar/</tt> and the environment variable named <tt>STATUS</tt> is set |
| to the value "java". |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteRule ^(.*)/<b>S=([^/]+)</b>/(.*) $1/$3 [E=<b>STATUS:$2</b>] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Virtual User Hosts</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Assume that you want to provide <tt>www.<b>username</b>.host.domain.com</tt> |
| for the homepage of username via just DNS A records to the same machine and |
| without any virtualhosts on this machine. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| For HTTP/1.0 requests there is no solution, but for HTTP/1.1 requests which |
| contain a Host: HTTP header we can use the following ruleset to rewrite |
| <tt>http://www.username.host.com/anypath</tt> internally to |
| <tt>/home/username/anypath</tt>: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteCond %{<b>HTTP_HOST</b>} ^www\.<b>[^.]+</b>\.host\.com$ |
| RewriteRule ^(.+) %{HTTP_HOST}$1 [C] |
| RewriteRule ^www\.<b>([^.]+)</b>\.host\.com(.*) /home/<b>$1</b>$2 |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Redirect Homedirs For Foreigners</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| We want to redirect homedir URLs to another webserver |
| <tt>www.somewhere.com</tt> when the requesting user does not stay in the local |
| domain <tt>ourdomain.com</tt>. This is sometimes used in virtual host |
| contexts. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| Just a rewrite condition: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteCond %{REMOTE_HOST} <b>!^.+\.ourdomain\.com$</b> |
| RewriteRule ^(/~.+) http://www.somewhere.com/$1 [R,L] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Redirect Failing URLs To Other Webserver</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| A typical FAQ about URL rewriting is how to redirect failing requests on |
| webserver A to webserver B. Usually this is done via ErrorDocument |
| CGI-scripts in Perl, but there is also a mod_rewrite solution. But notice that |
| this is less performant than using a ErrorDocument CGI-script! |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| The first solution has the best performance but less flexibility and is less |
| error safe: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteCond /your/docroot/%{REQUEST_FILENAME} <b>!-f</b> |
| RewriteRule ^(.+) http://<b>webserverB</b>.dom/$1 |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| The problem here is that this will only work for pages inside the |
| DocumentRoot. While you can add more Conditions (for instance to also handle |
| homedirs, etc.) there is better variant: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteCond %{REQUEST_URI} <b>!-U</b> |
| RewriteRule ^(.+) http://<b>webserverB</b>.dom/$1 |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| This uses the URL look-ahead feature of mod_rewrite. The result is that this |
| will work for all types of URLs and is a safe way. But it does a performance |
| impact on the webserver, because for every request there is one more internal |
| subrequest. So, if your webserver runs on a powerful CPU, use this one. If it |
| is a slow machine, use the first approach or better a ErrorDocument |
| CGI-script. |
| |
| </DL> |
| |
| <P> |
| <H2>Extended Redirection</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Sometimes we need more control (concerning the character escaping mechanism) |
| of URLs on redirects. Usually the Apache kernels URL escape function also |
| escapes anchors, i.e. URLs like "url#anchor". You cannot use this directly on |
| redirects with mod_rewrite because the uri_escape() function of Apache would |
| also escape the hash character. How can we redirect to such a URL? |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We have to use a kludge by the use of a NPH-CGI script which does the redirect |
| itself. Because here no escaping is done (NPH=non-parseable headers). First |
| we introduce a new URL scheme <tt>xredirect:</tt> by the following per-server |
| config-line (should be one of the last rewrite rules): |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteRule ^xredirect:(.+) /path/to/nph-xredirect.cgi/$1 \ |
| [T=application/x-httpd-cgi,L] |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| This forces all URLs prefixed with <tt>xredirect:</tt> to be piped through the |
| <tt>nph-xredirect.cgi</tt> program. And this program just looks like: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| <PRE> |
| #!/path/to/perl |
| ## |
| ## nph-xredirect.cgi -- NPH/CGI script for extended redirects |
| ## Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved. |
| ## |
| |
| $| = 1; |
| $url = $ENV{'PATH_INFO'}; |
| |
| print "HTTP/1.0 302 Moved Temporarily\n"; |
| print "Server: $ENV{'SERVER_SOFTWARE'}\n"; |
| print "Location: $url\n"; |
| print "Content-type: text/html\n"; |
| print "\n"; |
| print "<html>\n"; |
| print "<head>\n"; |
| print "<title>302 Moved Temporarily (EXTENDED)</title>\n"; |
| print "</head>\n"; |
| print "<body>\n"; |
| print "<h1>Moved Temporarily (EXTENDED)</h1>\n"; |
| print "The document has moved <a href=\"$url\">here</a>.<p>\n"; |
| print "</body>\n"; |
| print "</html>\n"; |
| |
| ##EOF## |
| </PRE> |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| This provides you with the functionality to do redirects to all URL schemes, |
| i.e. including the one which are not directly accepted by mod_rewrite. For |
| instance you can now also redirect to <tt>news:newsgroup</tt> via |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteRule ^anyurl xredirect:news:newsgroup |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| Notice: You have not to put [R] or [R,L] to the above rule because the |
| <tt>xredirect:</tt> need to be expanded later by our special "pipe through" |
| rule above. |
| |
| </DL> |
| |
| <P> |
| <H2>Archive Access Multiplexer</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Do you know the great CPAN (Comprehensive Perl Archive Network) under <a |
| href="http://www.perl.com/CPAN">http://www.perl.com/CPAN</a>? This does a |
| redirect to one of several FTP servers around the world which carry a CPAN |
| mirror and is approximately near the location of the requesting client. |
| Actually this can be called an FTP access multiplexing service. While CPAN |
| runs via CGI scripts, how can a similar approach implemented via mod_rewrite? |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| First we notice that from version 3.0.0 mod_rewrite can also use the "ftp:" |
| scheme on redirects. And second, the location approximation can be done by a |
| rewritemap over the top-level domain of the client. With a tricky chained |
| ruleset we can use this top-level domain as a key to our multiplexing map. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteMap multiplex txt:/path/to/map.cxan |
| RewriteRule ^/CxAN/(.*) %{REMOTE_HOST}::$1 [C] |
| RewriteRule ^.+\.<b>([a-zA-Z]+)</b>::(.*)$ ${multiplex:<b>$1</b>|ftp.default.dom}$2 [R,L] |
| </PRE></TD></TR></TABLE> |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| ## |
| ## map.cxan -- Multiplexing Map for CxAN |
| ## |
| |
| de ftp://ftp.cxan.de/CxAN/ |
| uk ftp://ftp.cxan.uk/CxAN/ |
| com ftp://ftp.cxan.com/CxAN/ |
| : |
| ##EOF## |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Time-Dependend Rewriting</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| When tricks like time-dependend content should happen a lot of webmasters |
| still use CGI scripts which do for instance redirects to specialized pages. |
| How can it be done via mod_rewrite? |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| There are a lot of variables named <tt>TIME_xxx</tt> for rewrite conditions. |
| In conjunction with the special lexicographic comparison patterns <STRING, |
| >STRING and =STRING we can do time-dependend redirects: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteCond %{TIME_HOUR}%{TIME_MIN} >0700 |
| RewriteCond %{TIME_HOUR}%{TIME_MIN} <1900 |
| RewriteRule ^foo\.html$ foo.day.html |
| RewriteRule ^foo\.html$ foo.night.html |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| This provides the content of <tt>foo.day.html</tt> under the URL |
| <tt>foo.html</tt> from 07:00-19:00 and at the remaining time the contents of |
| <tt>foo.night.html</tt>. Just a nice feature for a homepage... |
| |
| </DL> |
| |
| <P> |
| <H2>Backward Compatibility for YYYY to XXXX migration</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| How can we make URLs backward compatible (still existing virtually) after |
| migrating document.YYYY to document.XXXX, e.g. after translating a bunch of |
| .html files to .phtml? |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We just rewrite the name to its basename and test for existence of the new |
| extension. If it exists, we take that name, else we rewrite the URL to its |
| original state. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| # backward compatibility ruleset for |
| # rewriting document.html to document.phtml |
| # when and only when document.phtml exists |
| # but no longer document.html |
| RewriteEngine on |
| RewriteBase /~quux/ |
| # parse out basename, but remember the fact |
| RewriteRule ^(.*)\.html$ $1 [C,E=WasHTML:yes] |
| # rewrite to document.phtml if exists |
| RewriteCond %{REQUEST_FILENAME}.phtml -f |
| RewriteRule ^(.*)$ $1.phtml [S=1] |
| # else reverse the previous basename cutout |
| RewriteCond %{ENV:WasHTML} ^yes$ |
| RewriteRule ^(.*)$ $1.html |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <H1>Content Handling</H1> |
| |
| <P> |
| <H2>From Old to New (intern)</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Assume we have recently renamed the page <tt>bar.html</tt> to |
| <tt>foo.html</tt> and now want to provide the old URL for backward |
| compatibility. Actually we want that users of the old URL even not recognize |
| that the pages was renamed. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We rewrite the old URL to the new one internally via the following rule: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteBase /~quux/ |
| RewriteRule ^<b>foo</b>\.html$ <b>bar</b>.html |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>From Old to New (extern)</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Assume again that we have recently renamed the page <tt>bar.html</tt> to |
| <tt>foo.html</tt> and now want to provide the old URL for backward |
| compatibility. But this time we want that the users of the old URL get hinted |
| to the new one, i.e. their browsers Location field should change, too. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We force a HTTP redirect to the new URL which leads to a change of the |
| browsers and thus the users view: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteBase /~quux/ |
| RewriteRule ^<b>foo</b>\.html$ <b>bar</b>.html [<b>R</b>] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Browser Dependend Content</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| At least for important top-level pages it is sometimes necesarry to provide |
| the optimum of browser dependend content, i.e. one has to provide a maximum |
| version for the latest Netscape variants, a minimum version for the Lynx |
| browsers and a average feature version for all others. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We cannot use content negotiation because the browsers do not provide their |
| type in that form. Instead we have to act on the HTTP header "User-Agent". |
| The following condig does the following: If the HTTP header "User-Agent" |
| begins with "Mozilla/3", the page <tt>foo.html</tt> is rewritten to |
| <tt>foo.NS.html</tt> and and the rewriting stops. If the browser is "Lynx" or |
| "Mozilla" of version 1 or 2 the URL becomes <tt>foo.20.html</tt>. All other |
| browsers receive page <tt>foo.32.html</tt>. This is done by the following |
| ruleset: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteCond %{HTTP_USER_AGENT} ^<b>Mozilla/3</b>.* |
| RewriteRule ^foo\.html$ foo.<b>NS</b>.html [<b>L</b>] |
| |
| RewriteCond %{HTTP_USER_AGENT} ^<b>Lynx/</b>.* [OR] |
| RewriteCond %{HTTP_USER_AGENT} ^<b>Mozilla/[12]</b>.* |
| RewriteRule ^foo\.html$ foo.<b>20</b>.html [<b>L</b>] |
| |
| RewriteRule ^foo\.html$ foo.<b>32</b>.html [<b>L</b>] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Dynamic Mirror</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Assume there are nice webpages on remote hosts we want to bring into our |
| namespace. For FTP servers we would use the <tt>mirror</tt> program which |
| actually maintains an explicit up-to-date copy of the remote data on the local |
| machine. For a webserver we could use the program <tt>webcopy</tt> which acts |
| similar via HTTP. But both techniques have one major drawback: The local copy |
| is always just as up-to-date as often we run the program. It would be much |
| better if the mirror is not a static one we have to establish explicitly. |
| Instead we want a dynamic mirror with data which gets updated automatically |
| when there is need (updated data on the remote host). |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| To provide this feature we map the remote webpage or even the complete remote |
| webarea to our namespace by the use of the <I>Proxy Throughput</I> feature |
| (flag [P]): |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteBase /~quux/ |
| RewriteRule ^<b>hotsheet/</b>(.*)$ <b>http://www.tstimpreso.com/hotsheet/</b>$1 [<b>P</b>] |
| </PRE></TD></TR></TABLE> |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteBase /~quux/ |
| RewriteRule ^<b>usa-news\.html</b>$ <b>http://www.quux-corp.com/news/index.html</b> [<b>P</b>] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Reverse Dynamic Mirror</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| ... |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteCond /mirror/of/remotesite/$1 -U |
| RewriteRule ^http://www\.remotesite\.com/(.*)$ /mirror/of/remotesite/$1 |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Retrieve Missing Data from Intranet</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| This is a tricky way of virtually running a corporates (external) Internet |
| webserver (<tt>www.quux-corp.dom</tt>), while actually keeping and maintaining |
| its data on a (internal) Intranet webserver |
| (<tt>www2.quux-corp.dom</tt>) which is protected by a firewall. The |
| trick is that on the external webserver we retrieve the requested data |
| on-the-fly from the internal one. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| First, we have to make sure that our firewall still protects the internal |
| webserver and that only the external webserver is allowed to retrieve data |
| from it. For a packet-filtering firewall we could for instance configure a |
| firewall ruleset like the following: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| <b>ALLOW</b> Host www.quux-corp.dom Port >1024 --> Host www2.quux-corp.dom Port <b>80</b> |
| <b>DENY</b> Host * Port * --> Host www2.quux-corp.dom Port <b>80</b> |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| Just adjust it to your actual configuration syntax. Now we can establish the |
| mod_rewrite rules which request the missing data in the background through the |
| proxy throughput feature: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteRule ^/~([^/]+)/?(.*) /home/$1/.www/$2 |
| RewriteCond %{REQUEST_FILENAME} <b>!-f</b> |
| RewriteCond %{REQUEST_FILENAME} <b>!-d</b> |
| RewriteRule ^/home/([^/]+)/.www/?(.*) http://<b>www2</b>.quux-corp.dom/~$1/pub/$2 [<b>P</b>] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Load Balancing</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Suppose we want to load balance the traffic to <tt>www.foo.com</tt> over |
| <tt>www[0-5].foo.com</tt> (a total of 6 servers). How can this be done? |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| There are a lot of possible solutions for this problem. We will discuss first |
| a commonly known DNS-based variant and then the special one with mod_rewrite: |
| |
| <ol> |
| <li><b>DNS Round-Robin</b> |
| |
| <P> |
| The simplest method for load-balancing is to use the DNS round-robin feature |
| of BIND. Here you just configure <tt>www[0-9].foo.com</tt> as usual in your |
| DNS with A(address) records, e.g. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| www0 IN A 1.2.3.1 |
| www1 IN A 1.2.3.2 |
| www2 IN A 1.2.3.3 |
| www3 IN A 1.2.3.4 |
| www4 IN A 1.2.3.5 |
| www5 IN A 1.2.3.6 |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| Then you additionally add the following entry: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| www IN CNAME www0.foo.com. |
| IN CNAME www1.foo.com. |
| IN CNAME www2.foo.com. |
| IN CNAME www3.foo.com. |
| IN CNAME www4.foo.com. |
| IN CNAME www5.foo.com. |
| IN CNAME www6.foo.com. |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| Notice that this seems wrong, but is actually an intended feature of BIND and |
| can be used in this way. However, now when <tt>www.foo.com</tt> gets resolved, |
| BIND gives out <tt>www0-www6</tt> - but in a slightly permutated/rotated order |
| every time. This way the clients are spread over the various servers. |
| |
| But notice that this not a perfect load balancing scheme, because DNS resolve |
| information gets cached by the other nameservers on the net, so once a client |
| has resolved <tt>www.foo.com</tt> to a particular <tt>wwwN.foo.com</tt>, all |
| subsequent requests also go to this particular name <tt>wwwN.foo.com</tt>. But |
| the final result is ok, because the total sum of the requests are really |
| spread over the various webservers. |
| |
| <P> |
| <li><b>DNS Load-Balancing</b> |
| |
| <P> |
| A sophisticated DNS-based method for load-balancing is to use the program |
| <tt>lbnamed</tt> which can be found at <a |
| href="http://www.stanford.edu/~schemers/docs/lbnamed/lbnamed.html">http://www.stanford.edu/~schemers/docs/lbnamed/lbnamed.html</a>. |
| It is a Perl 5 program in conjunction with auxilliary tools which provides a |
| real load-balancing for DNS. |
| |
| <P> |
| <li><b>Proxy Throughput Round-Robin</b> |
| |
| <P> |
| In this variant we use mod_rewrite and its proxy throughput feature. First we |
| dedicate <tt>www0.foo.com</tt> to be actually <tt>www.foo.com</tt> by using a |
| single |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| www IN CNAME www0.foo.com. |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| entry in the DNS. Then we convert <tt>www0.foo.com</tt> to a proxy-only |
| server, i.e. we configure this machine so all arriving URLs are just pushed |
| through the internal proxy to one of the 5 other servers (<tt>www1-www5</tt>). |
| To accomplish this we first establish a ruleset which contacts a load |
| balancing script <tt>lb.pl</tt> for all URLs. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteMap lb prg:/path/to/lb.pl |
| RewriteRule ^/(.+)$ ${lb:$1} [P,L] |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| Then we write <tt>lb.pl</tt>: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| #!/path/to/perl |
| ## |
| ## lb.pl -- load balancing script |
| ## |
| |
| $| = 1; |
| |
| $name = "www"; # the hostname base |
| $first = 1; # the first server (not 0 here, because 0 is myself) |
| $last = 5; # the last server in the round-robin |
| $domain = "foo.dom"; # the domainname |
| |
| $cnt = 0; |
| while (<STDIN>) { |
| $cnt = (($cnt+1) % ($last+1-$first)); |
| $server = sprintf("%s%d.%s", $name, $cnt+$first, $domain); |
| print "http://$server/$_"; |
| } |
| |
| ##EOF## |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| A last notice: Why is this useful? Seems like <tt>www0.foo.com</tt> still is |
| overloaded? The answer is yes, it is overloaded, but with plain proxy |
| throughput requests, only! All SSI, CGI, ePerl, etc. processing is completely |
| done on the other machines. This is the essential point. |
| |
| <P> |
| <li><b>Hardware/TCP Round-Robin</b> |
| |
| <P> |
| There is a hardware solution available, too. Cisco has a beast called |
| LocalDirector which does a load balancing at the TCP/IP level. Actually this |
| is some sort of a circuit level gateway in front of a webcluster. If you have |
| enough money and really need a solution with high performance, use this one. |
| |
| </ol> |
| |
| </DL> |
| |
| <P> |
| <H2>Reverse Proxy</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| ... |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| ## |
| ## apache-rproxy.conf -- Apache configuration for Reverse Proxy Usage |
| ## |
| |
| # server type |
| ServerType standalone |
| Port 8000 |
| MinSpareServers 16 |
| StartServers 16 |
| MaxSpareServers 16 |
| MaxClients 16 |
| MaxRequestsPerChild 100 |
| |
| # server operation parameters |
| KeepAlive on |
| MaxKeepAliveRequests 100 |
| KeepAliveTimeout 15 |
| Timeout 400 |
| IdentityCheck off |
| HostnameLookups off |
| |
| # paths to runtime files |
| PidFile /path/to/apache-rproxy.pid |
| LockFile /path/to/apache-rproxy.lock |
| ErrorLog /path/to/apache-rproxy.elog |
| CustomLog /path/to/apache-rproxy.dlog "%{%v/%T}t %h -> %{SERVER}e URL: %U" |
| |
| # unused paths |
| ServerRoot /tmp |
| DocumentRoot /tmp |
| CacheRoot /tmp |
| RewriteLog /dev/null |
| TransferLog /dev/null |
| TypesConfig /dev/null |
| AccessConfig /dev/null |
| ResourceConfig /dev/null |
| |
| # speed up and secure processing |
| <Directory /> |
| Options -FollowSymLinks -SymLinksIfOwnerMatch |
| AllowOverwrite None |
| </Directory> |
| |
| # the status page for monitoring the reverse proxy |
| <Location /rproxy-status> |
| SetHandler server-status |
| </Location> |
| |
| # enable the URL rewriting engine |
| RewriteEngine on |
| RewriteLogLevel 0 |
| |
| # define a rewriting map with value-lists where |
| # mod_rewrite randomly chooses a particular value |
| RewriteMap server rnd:/path/to/apache-rproxy.conf-servers |
| |
| # make sure the status page is handled locally |
| # and make sure no one uses our proxy except ourself |
| RewriteRule ^/apache-rproxy-status.* - [L] |
| RewriteRule ^(http|ftp)://.* - [F] |
| |
| # now choose the possible servers for particular URL types |
| RewriteRule ^/(.*\.(cgi|shtml))$ to://${server:dynamic}/$1 [S=1] |
| RewriteRule ^/(.*)$ to://${server:static}/$1 |
| |
| # and delegate the generated URL by passing it |
| # through the proxy module |
| RewriteRule ^to://([^/]+)/(.*) http://$1/$2 [E=SERVER:$1,P,L] |
| |
| # and make really sure all other stuff is forbidden |
| # when it should survive the above rules... |
| RewriteRule .* - [F] |
| |
| # enable the Proxy module without caching |
| ProxyRequests on |
| NoCache * |
| |
| # setup URL reverse mapping for redirect reponses |
| ProxyPassReverse / http://www1.foo.dom/ |
| ProxyPassReverse / http://www2.foo.dom/ |
| ProxyPassReverse / http://www3.foo.dom/ |
| ProxyPassReverse / http://www4.foo.dom/ |
| ProxyPassReverse / http://www5.foo.dom/ |
| ProxyPassReverse / http://www6.foo.dom/ |
| </PRE></TD></TR></TABLE> |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| ## |
| ## apache-rproxy.conf-servers -- Apache/mod_rewrite selection table |
| ## |
| |
| # list of backend servers which serve static |
| # pages (HTML files and Images, etc.) |
| static www1.foo.dom|www2.foo.dom|www3.foo.dom|www4.foo.dom |
| |
| # list of backend servers which serve dynamically |
| # generated page (CGI programs or mod_perl scripts) |
| dynamic www5.foo.dom|www6.foo.dom |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>New MIME-type, New Service</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| On the net there are a lot of nifty CGI programs. But their usage is usually |
| boring, so a lot of webmaster don't use them. Even Apache's Action handler |
| feature for MIME-types is only appropriate when the CGI programs don't need |
| special URLs (actually PATH_INFO and QUERY_STRINGS) as their input. |
| |
| First, let us configure a new file type with extension <tt>.scgi</tt> |
| (for secure CGI) which will be processed by the popular <tt>cgiwrap</tt> |
| program. The problem here is that for instance we use a Homogeneous URL Layout |
| (see above) a file inside the user homedirs has the URL |
| <tt>/u/user/foo/bar.scgi</tt>. But <tt>cgiwrap</tt> needs the URL in the form |
| <tt>/~user/foo/bar.scgi/</tt>. The following rule solves the problem: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteRule ^/[uge]/<b>([^/]+)</b>/\.www/(.+)\.scgi(.*) ... |
| ... /internal/cgi/user/cgiwrap/~<b>$1</b>/$2.scgi$3 [NS,<b>T=application/x-http-cgi</b>] |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| Or assume we have some more nifty programs: |
| <tt>wwwlog</tt> (which displays the <tt>access.log</tt> for a URL subtree and |
| <tt>wwwidx</tt> (which runs Glimpse on a URL subtree). We have to |
| provide the URL area to these programs so they know on which area |
| they have to act on. But usually this ugly, because they are all the |
| times still requested from that areas, i.e. typically we would run |
| the <tt>swwidx</tt> program from within <tt>/u/user/foo/</tt> via |
| hyperlink to |
| |
| <P><PRE> |
| /internal/cgi/user/swwidx?i=/u/user/foo/ |
| </PRE><P> |
| |
| which is ugly. Because we have to hard-code <b>both</b> the location of the |
| area <b>and</b> the location of the CGI inside the hyperlink. When we have to |
| reorganise or area, we spend a lot of time changing the various hyperlinks. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| The solution here is to provide a special new URL format which automatically |
| leads to the proper CGI invocation. We configure the following: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteRule ^/([uge])/([^/]+)(/?.*)/\* /internal/cgi/user/wwwidx?i=/$1/$2$3/ |
| RewriteRule ^/([uge])/([^/]+)(/?.*):log /internal/cgi/user/wwwlog?f=/$1/$2$3 |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| Now the hyperlink to search at <tt>/u/user/foo/</tt> reads only |
| |
| <P><PRE> |
| href="*" |
| </PRE><P> |
| |
| which internally gets automatically transformed to |
| |
| <P><PRE> |
| /internal/cgi/user/wwwidx?i=/u/user/foo/ |
| </PRE><P> |
| |
| The same approach leads to an invocation for the access log CGI |
| program when the hyperlink <tt>:log</tt> gets used. |
| |
| </DL> |
| |
| <P> |
| <H2>From Static to Dynamic</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| How can we transform a static page <tt>foo.html</tt> into a dynamic variant |
| <tt>foo.cgi</tt> in a seemless way, i.e. without notice by the browser/user. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We just rewrite the URL to the CGI-script and force the correct MIME-type so |
| it gets really run as a CGI-script. This way a request to |
| <tt>/~quux/foo.html</tt> internally leads to the invokation of |
| <tt>/~quux/foo.cgi</tt>. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteBase /~quux/ |
| RewriteRule ^foo\.<b>html</b>$ foo.<b>cgi</b> [T=<b>application/x-httpd-cgi</b>] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>On-the-fly Content-Regeneration</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Here comes a really esoteric feature: Dynamically generated but statically |
| served pages, i.e. pages should be delivered as pur static pages (read from |
| the filesystem and just passed through), but they have to be generated |
| dynamically by the webserver if missing. This way you can have CGI-generated |
| pages which are statically unless one (or a cronjob) removes the static |
| contents. Then the contents gets refreshed. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| This is done via the following ruleset: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteCond %{REQUEST_FILENAME} <b>!-s</b> |
| RewriteCond ^page\.<b>html</b>$ page.<b>cgi</b> [T=application/x-httpd-cgi,L] |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| Here a request to <tt>page.html</tt> leads to a internal run of a |
| corresponding <tt>page.cgi</tt> if <tt>page.html</tt> is still missing or has |
| filesize null. The trick here is that <tt>page.cgi</tt> is a usual CGI script |
| which (additionally to its STDOUT) writes its output to the file |
| <tt>page.html</tt>. Once it was run, the server sends out the data of |
| <tt>page.html</tt>. When the webmaster wants to force a refresh the contents, |
| he just removes <tt>page.html</tt> (usually done by a cronjob). |
| |
| </DL> |
| |
| <P> |
| <H2>Document With Autorefresh</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Wouldn't it be nice while creating a complex webpage if the webbrowser would |
| automatically refresh the page every time we write a new version from within |
| our editor? Impossible? |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| No! We just combine the MIME multipart feature, the webserver NPH feature and |
| the URL manipulation power of mod_rewrite. First, we establish a new URL |
| feature: Adding just <tt>:refresh</tt> to any URL causes this to be refreshed |
| every time it gets updated on the filesystem. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteRule ^(/[uge]/[^/]+/?.*):refresh /internal/cgi/apache/nph-refresh?f=$1 |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| Now when we reference the URL |
| |
| <P><PRE> |
| /u/foo/bar/page.html:refresh |
| </PRE><P> |
| |
| this leads to the internal invocation of the URL |
| |
| <P><PRE> |
| /internal/cgi/apache/nph-refresh?f=/u/foo/bar/page.html |
| </PRE><P> |
| |
| The only missing part is the NPH-CGI script. Although one would usually say |
| "left as an exercise to the reader" ;-) I will provide this, too. |
| |
| <P><PRE> |
| #!/sw/bin/perl |
| ## |
| ## nph-refresh -- NPH/CGI script for auto refreshing pages |
| ## Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved. |
| ## |
| $| = 1; |
| |
| # split the QUERY_STRING variable |
| @pairs = split(/&/, $ENV{'QUERY_STRING'}); |
| foreach $pair (@pairs) { |
| ($name, $value) = split(/=/, $pair); |
| $name =~ tr/A-Z/a-z/; |
| $name = 'QS_' . $name; |
| $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; |
| eval "\$$name = \"$value\""; |
| } |
| $QS_s = 1 if ($QS_s eq ''); |
| $QS_n = 3600 if ($QS_n eq ''); |
| if ($QS_f eq '') { |
| print "HTTP/1.0 200 OK\n"; |
| print "Content-type: text/html\n\n"; |
| print "&lt;b&gt;ERROR&lt;/b&gt;: No file given\n"; |
| exit(0); |
| } |
| if (! -f $QS_f) { |
| print "HTTP/1.0 200 OK\n"; |
| print "Content-type: text/html\n\n"; |
| print "&lt;b&gt;ERROR&lt;/b&gt;: File $QS_f not found\n"; |
| exit(0); |
| } |
| |
| sub print_http_headers_multipart_begin { |
| print "HTTP/1.0 200 OK\n"; |
| $bound = "ThisRandomString12345"; |
| print "Content-type: multipart/x-mixed-replace;boundary=$bound\n"; |
| &print_http_headers_multipart_next; |
| } |
| |
| sub print_http_headers_multipart_next { |
| print "\n--$bound\n"; |
| } |
| |
| sub print_http_headers_multipart_end { |
| print "\n--$bound--\n"; |
| } |
| |
| sub displayhtml { |
| local($buffer) = @_; |
| $len = length($buffer); |
| print "Content-type: text/html\n"; |
| print "Content-length: $len\n\n"; |
| print $buffer; |
| } |
| |
| sub readfile { |
| local($file) = @_; |
| local(*FP, $size, $buffer, $bytes); |
| ($x, $x, $x, $x, $x, $x, $x, $size) = stat($file); |
| $size = sprintf("%d", $size); |
| open(FP, "&lt;$file"); |
| $bytes = sysread(FP, $buffer, $size); |
| close(FP); |
| return $buffer; |
| } |
| |
| $buffer = &readfile($QS_f); |
| &print_http_headers_multipart_begin; |
| &displayhtml($buffer); |
| |
| sub mystat { |
| local($file) = $_[0]; |
| local($time); |
| |
| ($x, $x, $x, $x, $x, $x, $x, $x, $x, $mtime) = stat($file); |
| return $mtime; |
| } |
| |
| $mtimeL = &mystat($QS_f); |
| $mtime = $mtime; |
| for ($n = 0; $n &lt; $QS_n; $n++) { |
| while (1) { |
| $mtime = &mystat($QS_f); |
| if ($mtime ne $mtimeL) { |
| $mtimeL = $mtime; |
| sleep(2); |
| $buffer = &readfile($QS_f); |
| &print_http_headers_multipart_next; |
| &displayhtml($buffer); |
| sleep(5); |
| $mtimeL = &mystat($QS_f); |
| last; |
| } |
| sleep($QS_s); |
| } |
| } |
| |
| &print_http_headers_multipart_end; |
| |
| exit(0); |
| |
| ##EOF## |
| </PRE> |
| |
| </DL> |
| |
| <P> |
| <H2>Mass Virtual Hosting</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| The <tt><VirtualHost></tt> feature of Apache is nice and works great |
| when you just have a few dozens virtual hosts. But when you are an ISP and |
| have hundreds of virtual hosts to provide this feature is not the best choice. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| To provide this feature we map the remote webpage or even the complete remote |
| webarea to our namespace by the use of the <I>Proxy Throughput</I> feature |
| (flag [P]): |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| ## |
| ## vhost.map |
| ## |
| www.vhost1.dom:80 /path/to/docroot/vhost1 |
| www.vhost2.dom:80 /path/to/docroot/vhost2 |
| : |
| www.vhostN.dom:80 /path/to/docroot/vhostN |
| </PRE></TD></TR></TABLE> |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| ## |
| ## httpd.conf |
| ## |
| : |
| # use the canonical hostname on redirects, etc. |
| UseCanonicalName on |
| |
| : |
| # add the virtual host in front of the CLF-format |
| CustomLog /path/to/access_log "%{VHOST}e %h %l %u %t \"%r\" %>s %b" |
| : |
| |
| # enable the rewriting engine in the main server |
| RewriteEngine on |
| |
| # define two maps: one for fixing the URL and one which defines |
| # the available virtual hosts with their corresponding |
| # DocumentRoot. |
| RewriteMap lowercase int:tolower |
| RewriteMap vhost txt:/path/to/vhost.map |
| |
| # Now do the actual virtual host mapping |
| # via a huge and complicated single rule: |
| # |
| # 1. make sure we don't map for common locations |
| RewriteCond %{REQUEST_URL} !^/commonurl1/.* |
| RewriteCond %{REQUEST_URL} !^/commonurl2/.* |
| : |
| RewriteCond %{REQUEST_URL} !^/commonurlN/.* |
| # |
| # 2. make sure we have a Host header, because |
| # currently our approach only supports |
| # virtual hosting through this header |
| RewriteCond %{HTTP_HOST} !^$ |
| # |
| # 3. lowercase the hostname |
| RewriteCond ${lowercase:%{HTTP_HOST}|NONE} ^(.+)$ |
| # |
| # 4. lookup this hostname in vhost.map and |
| # remember it only when it is a path |
| # (and not "NONE" from above) |
| RewriteCond ${vhost:%1} ^(/.*)$ |
| # |
| # 5. finally we can map the URL to its docroot location |
| # and remember the virtual host for logging puposes |
| RewriteRule ^/(.*)$ %1/$1 [E=VHOST:${lowercase:%{HTTP_HOST}}] |
| : |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <H1>Access Restriction</H1> |
| |
| <P> |
| <H2>Blocking of Robots</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| How can we block a really annoying robot from retrieving pages of a specific |
| webarea? A <tt>/robots.txt</tt> file containing entries of the "Robot |
| Exclusion Protocol" is typically not enough to get rid of such a robot. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We use a ruleset which forbids the URLs of the webarea |
| <tt>/~quux/foo/arc/</tt> (perhaps a very deep directory indexed area where the |
| robot traversal would create big server load). We have to make sure that we |
| forbid access only to the particular robot, i.e. just forbidding the host |
| where the robot runs is not enough. This would block users from this host, |
| too. We accomplish this by also matching the User-Agent HTTP header |
| information. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteCond %{HTTP_USER_AGENT} ^<b>NameOfBadRobot</b>.* |
| RewriteCond %{REMOTE_ADDR} ^<b>123\.45\.67\.[8-9]</b>$ |
| RewriteRule ^<b>/~quux/foo/arc/</b>.+ - [<b>F</b>] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Blocked Inline-Images</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Assume we have under http://www.quux-corp.de/~quux/ some pages with inlined |
| GIF graphics. These graphics are nice, so others directly incorporate them via |
| hyperlinks to their pages. We don't like this practice because it adds useless |
| traffic to our server. |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| While we cannot 100% protect the images from inclusion, we |
| can at least restrict the cases where the browser sends |
| a HTTP Referer header. |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteCond %{HTTP_REFERER} <b>!^$</b> |
| RewriteCond %{HTTP_REFERER} !^http://www.quux-corp.de/~quux/.*$ [NC] |
| RewriteRule <b>.*\.gif$</b> - [F] |
| </PRE></TD></TR></TABLE> |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteCond %{HTTP_REFERER} !^$ |
| RewriteCond %{HTTP_REFERER} !.*/foo-with-gif\.html$ |
| RewriteRule <b>^inlined-in-foo\.gif$</b> - [F] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Host Deny</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| How can we forbid a list of externally configured hosts from using our server? |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| |
| For Apache >= 1.3b6: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteMap hosts-deny txt:/path/to/hosts.deny |
| RewriteCond ${hosts-deny:%{REMOTE_HOST}|NOT-FOUND} !=NOT-FOUND [OR] |
| RewriteCond ${hosts-deny:%{REMOTE_ADDR}|NOT-FOUND} !=NOT-FOUND |
| RewriteRule ^/.* - [F] |
| </PRE></TD></TR></TABLE><P> |
| |
| For Apache <= 1.3b6: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteMap hosts-deny txt:/path/to/hosts.deny |
| RewriteRule ^/(.*)$ ${hosts-deny:%{REMOTE_HOST}|NOT-FOUND}/$1 |
| RewriteRule !^NOT-FOUND/.* - [F] |
| RewriteRule ^NOT-FOUND/(.*)$ ${hosts-deny:%{REMOTE_ADDR}|NOT-FOUND}/$1 |
| RewriteRule !^NOT-FOUND/.* - [F] |
| RewriteRule ^NOT-FOUND/(.*)$ /$1 |
| </PRE></TD></TR></TABLE> |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| ## |
| ## hosts.deny |
| ## |
| ## ATTENTION! This is a map, not a list, even when we treat it as such. |
| ## mod_rewrite parses it for key/value pairs, so at least a |
| ## dummy value "-" must be present for each entry. |
| ## |
| |
| 193.102.180.41 - |
| bsdti1.sdm.de - |
| 192.76.162.40 - |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Proxy Deny</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| How can we forbid a certain host or even a user of a special host from using |
| the Apache proxy? |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We first have to make sure mod_rewrite is below(!) mod_proxy in the |
| <tt>Configuration</tt> file when compiling the Apache webserver. This way it |
| gets called _before_ mod_proxy. Then we configure the following for a |
| host-dependend deny... |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteCond %{REMOTE_HOST} <b>^badhost\.mydomain\.com$</b> |
| RewriteRule !^http://[^/.]\.mydomain.com.* - [F] |
| </PRE></TD></TR></TABLE> |
| |
| <P>...and this one for a user@host-dependend deny: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <b>^badguy@badhost\.mydomain\.com$</b> |
| RewriteRule !^http://[^/.]\.mydomain.com.* - [F] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Special Authentication Variant</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| Sometimes a very special authentication is needed, for instance a |
| authentication which checks for a set of explicitly configured users. Only |
| these should receive access and without explicit prompting (which would occur |
| when using the Basic Auth via mod_access). |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| We use a list of rewrite conditions to exclude all except our friends: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <b>!^friend1@client1.quux-corp\.com$</b> |
| RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <b>!^friend2</b>@client2.quux-corp\.com$ |
| RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <b>!^friend3</b>@client3.quux-corp\.com$ |
| RewriteRule ^/~quux/only-for-friends/ - [F] |
| </PRE></TD></TR></TABLE> |
| |
| </DL> |
| |
| <P> |
| <H2>Referer-based Deflector</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| How can we program a flexible URL Deflector which acts on the "Referer" HTTP |
| header and can be configured with as many referring pages as we like? |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| Use the following really tricky ruleset... |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteMap deflector txt:/path/to/deflector.map |
| |
| RewriteCond %{HTTP_REFERER} !="" |
| RewriteCond ${deflector:%{HTTP_REFERER}} ^-$ |
| RewriteRule ^.* %{HTTP_REFERER} [R,L] |
| |
| RewriteCond %{HTTP_REFERER} !="" |
| RewriteCond ${deflector:%{HTTP_REFERER}|NOT-FOUND} !=NOT-FOUND |
| RewriteRule ^.* ${deflector:%{HTTP_REFERER}} [R,L] |
| </PRE></TD></TR></TABLE> |
| |
| <P>... |
| in conjunction with a corresponding rewrite map: |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| ## |
| ## deflector.map |
| ## |
| |
| http://www.badguys.com/bad/index.html - |
| http://www.badguys.com/bad/index2.html - |
| http://www.badguys.com/bad/index3.html http://somewhere.com/ |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| This automatically redirects the request back to the referring page (when "-" |
| is used as the value in the map) or to a specific URL (when an URL is |
| specified in the map as the second argument). |
| |
| </DL> |
| |
| <H1>Other</H1> |
| |
| <P> |
| <H2>External Rewriting Engine</H2> |
| <P> |
| |
| <DL> |
| <DT><STRONG>Description:</STRONG> |
| <DD> |
| A FAQ: How can we solve the FOO/BAR/QUUX/etc. problem? There seems no solution |
| by the use of mod_rewrite... |
| |
| <P> |
| <DT><STRONG>Solution:</STRONG> |
| <DD> |
| Use an external rewrite map, i.e. a program which acts like a rewrite map. It |
| is run once on startup of Apache receives the requested URLs on STDIN and has |
| to put the resulting (usually rewritten) URL on STDOUT (same order!). |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| RewriteEngine on |
| RewriteMap quux-map <b>prg:</b>/path/to/map.quux.pl |
| RewriteRule ^/~quux/(.*)$ /~quux/<b>${quux-map:$1}</b> |
| </PRE></TD></TR></TABLE> |
| |
| <P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE> |
| #!/path/to/perl |
| |
| # disable buffered I/O which would lead |
| # to deadloops for the Apache server |
| $| = 1; |
| |
| # read URLs one per line from stdin and |
| # generate substitution URL on stdout |
| while (<>) { |
| s|^foo/|bar/|; |
| print $_; |
| } |
| </PRE></TD></TR></TABLE> |
| |
| <P> |
| This is a demonstration-only example and just rewrites all URLs |
| <tt>/~quux/foo/...</tt> to <tt>/~quux/bar/...</tt>. Actually you can program |
| whatever you like. But notice that while such maps can be <b>used</b> also by |
| an average user, only the system administrator can <b>define</b> it. |
| |
| </DL> |
| |
| <!--#include virtual="footer.html" --> |
| </BLOCKQUOTE> |
| </BODY> |
| </HTML> |