mirror of
https://github.com/apache/httpd.git
synced 2025-05-17 15:21:13 +03:00
Thanks to Martin for hint. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@84610 13f79535-47bb-0310-9956-ffa450edef68
1907 lines
62 KiB
HTML
1907 lines
62 KiB
HTML
<!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>
|
|
<TABLE BGCOLOR="#FFE0E0" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD>
|
|
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 <CODE>.htaccess</CODE> context instead of per-server context. Always try
|
|
to understand what a particular ruleset really does before you use it. It
|
|
avoid problems.
|
|
</TD></TR></TABLE>
|
|
|
|
<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 <CODE>/~user</CODE> by the canonical <CODE>/u/user</CODE> and
|
|
fix a missing trailing slash for <CODE>/u/user</CODE>.
|
|
|
|
<P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE>
|
|
RewriteRule ^/<STRONG>~</STRONG>([^/]+)/?(.*) /<STRONG>u</STRONG>/$1/$2 [<STRONG>R</STRONG>]
|
|
RewriteRule ^/([uge])/(<STRONG>[^/]+</STRONG>)$ /$1/$2<STRONG>/</STRONG> [<STRONG>R</STRONG>]
|
|
</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
|
|
``<CODE>/</CODE>''. 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 <CODE>/e/www/</CODE> (the homepage for WWW), <CODE>/e/sww/</CODE> (the
|
|
homepage for the Intranet) etc. Now because the data of the DocumentRoot stays
|
|
at <CODE>/e/www/</CODE> 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 <CODE>/</CODE> to <CODE>/e/www/</CODE>. While is seems
|
|
trivial it is actually trivial with mod_rewrite, only. Because the typical
|
|
old mechanisms of URL <EM>Aliases</EM> (as provides by mod_alias and friends)
|
|
only used <EM>prefix</EM> 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 <STRONG>^/$</STRONG> /e/www/ [<STRONG>R</STRONG>]
|
|
</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 <CODE>/~quux/foo</CODE> instead of
|
|
<CODE>/~quux/foo/</CODE> then the server searches for a <EM>file</EM> named
|
|
<CODE>foo</CODE>. 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
|
|
<CODE>image.gif</CODE> in <CODE>/~quux/foo/index.html</CODE> would become
|
|
<CODE>/~quux/image.gif</CODE> 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<STRONG>$</STRONG> foo<STRONG>/</STRONG> [<STRONG>R</STRONG>]
|
|
</PRE></TD></TR></TABLE>
|
|
|
|
<P>
|
|
The crazy and lazy can even do the following in the top-level
|
|
<CODE>.htaccess</CODE> 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} <STRONG>-d</STRONG>
|
|
RewriteRule ^(.+<STRONG>[^/]</STRONG>)$ $1<STRONG>/</STRONG> [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 <EM>independed</EM>! 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 <CODE>map.xxx-to-host</CODE>. 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/<STRONG>([^/]+)</STRONG>/?(.*) http://<STRONG>${user-to-host:$1|server0}</STRONG>/u/$1/$2
|
|
RewriteRule ^/g/<STRONG>([^/]+)</STRONG>/?(.*) http://<STRONG>${group-to-host:$1|server0}</STRONG>/g/$1/$2
|
|
RewriteRule ^/e/<STRONG>([^/]+)</STRONG>/?(.*) http://<STRONG>${entity-to-host:$1|server0}</STRONG>/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 <CODE>/~user/anypath</CODE> URLs to
|
|
<CODE>http://newserver/~user/anypath</CODE>.
|
|
|
|
<P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE>
|
|
RewriteEngine on
|
|
RewriteRule ^/~(.+) http://<STRONG>newserver</STRONG>/~$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, <CODE>/~foo/anypath</CODE> is
|
|
<CODE>/home/<STRONG>f</STRONG>/foo/.www/anypath</CODE> while <CODE>/~bar/anypath</CODE> is
|
|
<CODE>/home/<STRONG>b</STRONG>/bar/.www/anypath</CODE>.
|
|
|
|
<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 ^/~(<STRONG>([a-z])</STRONG>[a-z0-9]+)(.*) /home/<STRONG>$2</STRONG>/$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 <CODE>RewriteRules</CODE> to get a smooth look and feel on the Web
|
|
while its data structure is never touched or adjusted.
|
|
|
|
Background: <STRONG><EM>net.sw</EM></STRONG> 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 archive public to the world via a
|
|
nice Web interface. "Nice" means that I wanted to
|
|
offer an 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 stuff 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
|
|
<CODE>/e/netsw/.www/</CODE> 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 <CODE>DATA/</CODE> subdirectory holds the above directory structure, i.e. the
|
|
real <STRONG><EM>net.sw</EM></STRONG> stuff and gets automatically updated via
|
|
<CODE>rdist</CODE> 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 <CODE>DATA/</CODE>
|
|
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 <CODE>/net.sw/</CODE> to the internal path <CODE>/e/netsw</CODE>:
|
|
|
|
<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 <CODE>/e/netsw/.www/.wwwacl</CODE>:
|
|
|
|
<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 <CODE>imagemap</CODE> program to work under Apache with the modern
|
|
<CODE>mod_imap</CODE>. The problem is that there are a lot of
|
|
hyperlinks around which reference the <CODE>imagemap</CODE> program via
|
|
<CODE>/cgi-bin/imagemap/path/to/page.map</CODE>. Under Apache this
|
|
has to read just <CODE>/path/to/page.map</CODE>.
|
|
|
|
<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/<STRONG>dir1</STRONG>/%{REQUEST_FILENAME} -f
|
|
RewriteRule ^(.+) /your/docroot/<STRONG>dir1</STRONG>/$1 [L]
|
|
|
|
# second try to find it in pub/...
|
|
# ...and if found stop and be happy:
|
|
RewriteCond /your/docroot/<STRONG>dir2</STRONG>/%{REQUEST_FILENAME} -f
|
|
RewriteRule ^(.+) /your/docroot/<STRONG>dir2</STRONG>/$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 <CODE>/foo/S=java/bar/</CODE> gets translated to
|
|
<CODE>/foo/bar/</CODE> and the environment variable named <CODE>STATUS</CODE> is set
|
|
to the value "java".
|
|
|
|
<P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE>
|
|
RewriteEngine on
|
|
RewriteRule ^(.*)/<STRONG>S=([^/]+)</STRONG>/(.*) $1/$3 [E=<STRONG>STATUS:$2</STRONG>]
|
|
</PRE></TD></TR></TABLE>
|
|
|
|
</DL>
|
|
|
|
<P>
|
|
<H2>Virtual User Hosts</H2>
|
|
<P>
|
|
|
|
<DL>
|
|
<DT><STRONG>Description:</STRONG>
|
|
<DD>
|
|
Assume that you want to provide <CODE>www.<STRONG>username</STRONG>.host.domain.com</CODE>
|
|
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
|
|
<CODE>http://www.username.host.com/anypath</CODE> internally to
|
|
<CODE>/home/username/anypath</CODE>:
|
|
|
|
<P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE>
|
|
RewriteEngine on
|
|
RewriteCond %{<STRONG>HTTP_HOST</STRONG>} ^www\.<STRONG>[^.]+</STRONG>\.host\.com$
|
|
RewriteRule ^(.+) %{HTTP_HOST}$1 [C]
|
|
RewriteRule ^www\.<STRONG>([^.]+)</STRONG>\.host\.com(.*) /home/<STRONG>$1</STRONG>$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
|
|
<CODE>www.somewhere.com</CODE> when the requesting user does not stay in the local
|
|
domain <CODE>ourdomain.com</CODE>. 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} <STRONG>!^.+\.ourdomain\.com$</STRONG>
|
|
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} <STRONG>!-f</STRONG>
|
|
RewriteRule ^(.+) http://<STRONG>webserverB</STRONG>.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} <STRONG>!-U</STRONG>
|
|
RewriteRule ^(.+) http://<STRONG>webserverB</STRONG>.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 <CODE>xredirect:</CODE> 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 <CODE>xredirect:</CODE> to be piped through the
|
|
<CODE>nph-xredirect.cgi</CODE> 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 <CODE>news:newsgroup</CODE> 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
|
|
<CODE>xredirect:</CODE> 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 ^.+\.<STRONG>([a-zA-Z]+)</STRONG>::(.*)$ ${multiplex:<STRONG>$1</STRONG>|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 <CODE>TIME_xxx</CODE> 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 <CODE>foo.day.html</CODE> under the URL
|
|
<CODE>foo.html</CODE> from 07:00-19:00 and at the remaining time the contents of
|
|
<CODE>foo.night.html</CODE>. 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 <CODE>bar.html</CODE> to
|
|
<CODE>foo.html</CODE> 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 ^<STRONG>foo</STRONG>\.html$ <STRONG>bar</STRONG>.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 <CODE>bar.html</CODE> to
|
|
<CODE>foo.html</CODE> 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 ^<STRONG>foo</STRONG>\.html$ <STRONG>bar</STRONG>.html [<STRONG>R</STRONG>]
|
|
</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 <CODE>foo.html</CODE> is rewritten to
|
|
<CODE>foo.NS.html</CODE> and and the rewriting stops. If the browser is "Lynx" or
|
|
"Mozilla" of version 1 or 2 the URL becomes <CODE>foo.20.html</CODE>. All other
|
|
browsers receive page <CODE>foo.32.html</CODE>. This is done by the following
|
|
ruleset:
|
|
|
|
<P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE>
|
|
RewriteCond %{HTTP_USER_AGENT} ^<STRONG>Mozilla/3</STRONG>.*
|
|
RewriteRule ^foo\.html$ foo.<STRONG>NS</STRONG>.html [<STRONG>L</STRONG>]
|
|
|
|
RewriteCond %{HTTP_USER_AGENT} ^<STRONG>Lynx/</STRONG>.* [OR]
|
|
RewriteCond %{HTTP_USER_AGENT} ^<STRONG>Mozilla/[12]</STRONG>.*
|
|
RewriteRule ^foo\.html$ foo.<STRONG>20</STRONG>.html [<STRONG>L</STRONG>]
|
|
|
|
RewriteRule ^foo\.html$ foo.<STRONG>32</STRONG>.html [<STRONG>L</STRONG>]
|
|
</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 <CODE>mirror</CODE> 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 <CODE>webcopy</CODE> 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 ^<STRONG>hotsheet/</STRONG>(.*)$ <STRONG>http://www.tstimpreso.com/hotsheet/</STRONG>$1 [<STRONG>P</STRONG>]
|
|
</PRE></TD></TR></TABLE>
|
|
|
|
<P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE>
|
|
RewriteEngine on
|
|
RewriteBase /~quux/
|
|
RewriteRule ^<STRONG>usa-news\.html</STRONG>$ <STRONG>http://www.quux-corp.com/news/index.html</STRONG> [<STRONG>P</STRONG>]
|
|
</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 (<CODE>www.quux-corp.dom</CODE>), while actually keeping and maintaining
|
|
its data on a (internal) Intranet webserver
|
|
(<CODE>www2.quux-corp.dom</CODE>) 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>
|
|
<STRONG>ALLOW</STRONG> Host www.quux-corp.dom Port >1024 --> Host www2.quux-corp.dom Port <STRONG>80</STRONG>
|
|
<STRONG>DENY</STRONG> Host * Port * --> Host www2.quux-corp.dom Port <STRONG>80</STRONG>
|
|
</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} <STRONG>!-f</STRONG>
|
|
RewriteCond %{REQUEST_FILENAME} <STRONG>!-d</STRONG>
|
|
RewriteRule ^/home/([^/]+)/.www/?(.*) http://<STRONG>www2</STRONG>.quux-corp.dom/~$1/pub/$2 [<STRONG>P</STRONG>]
|
|
</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 <CODE>www.foo.com</CODE> over
|
|
<CODE>www[0-5].foo.com</CODE> (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><STRONG>DNS Round-Robin</STRONG>
|
|
|
|
<P>
|
|
The simplest method for load-balancing is to use the DNS round-robin feature
|
|
of BIND. Here you just configure <CODE>www[0-9].foo.com</CODE> 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 <CODE>www.foo.com</CODE> gets resolved,
|
|
BIND gives out <CODE>www0-www6</CODE> - 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 <CODE>www.foo.com</CODE> to a particular <CODE>wwwN.foo.com</CODE>, all
|
|
subsequent requests also go to this particular name <CODE>wwwN.foo.com</CODE>. But
|
|
the final result is ok, because the total sum of the requests are really
|
|
spread over the various webservers.
|
|
|
|
<P>
|
|
<li><STRONG>DNS Load-Balancing</STRONG>
|
|
|
|
<P>
|
|
A sophisticated DNS-based method for load-balancing is to use the program
|
|
<CODE>lbnamed</CODE> 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><STRONG>Proxy Throughput Round-Robin</STRONG>
|
|
|
|
<P>
|
|
In this variant we use mod_rewrite and its proxy throughput feature. First we
|
|
dedicate <CODE>www0.foo.com</CODE> to be actually <CODE>www.foo.com</CODE> 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 <CODE>www0.foo.com</CODE> 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 (<CODE>www1-www5</CODE>).
|
|
To accomplish this we first establish a ruleset which contacts a load
|
|
balancing script <CODE>lb.pl</CODE> 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 <CODE>lb.pl</CODE>:
|
|
|
|
<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 <CODE>www0.foo.com</CODE> 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><STRONG>Hardware/TCP Round-Robin</STRONG>
|
|
|
|
<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 <CODE>.scgi</CODE>
|
|
(for secure CGI) which will be processed by the popular <CODE>cgiwrap</CODE>
|
|
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
|
|
<CODE>/u/user/foo/bar.scgi</CODE>. But <CODE>cgiwrap</CODE> needs the URL in the form
|
|
<CODE>/~user/foo/bar.scgi/</CODE>. The following rule solves the problem:
|
|
|
|
<P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE>
|
|
RewriteRule ^/[uge]/<STRONG>([^/]+)</STRONG>/\.www/(.+)\.scgi(.*) ...
|
|
... /internal/cgi/user/cgiwrap/~<STRONG>$1</STRONG>/$2.scgi$3 [NS,<STRONG>T=application/x-http-cgi</STRONG>]
|
|
</PRE></TD></TR></TABLE>
|
|
|
|
<P>
|
|
Or assume we have some more nifty programs:
|
|
<CODE>wwwlog</CODE> (which displays the <CODE>access.log</CODE> for a URL subtree and
|
|
<CODE>wwwidx</CODE> (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 <CODE>swwidx</CODE> program from within <CODE>/u/user/foo/</CODE> via
|
|
hyperlink to
|
|
|
|
<P><PRE>
|
|
/internal/cgi/user/swwidx?i=/u/user/foo/
|
|
</PRE><P>
|
|
|
|
which is ugly. Because we have to hard-code <STRONG>both</STRONG> the location of the
|
|
area <STRONG>and</STRONG> 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 <CODE>/u/user/foo/</CODE> 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 <CODE>:log</CODE> gets used.
|
|
|
|
</DL>
|
|
|
|
<P>
|
|
<H2>From Static to Dynamic</H2>
|
|
<P>
|
|
|
|
<DL>
|
|
<DT><STRONG>Description:</STRONG>
|
|
<DD>
|
|
How can we transform a static page <CODE>foo.html</CODE> into a dynamic variant
|
|
<CODE>foo.cgi</CODE> 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
|
|
<CODE>/~quux/foo.html</CODE> internally leads to the invokation of
|
|
<CODE>/~quux/foo.cgi</CODE>.
|
|
|
|
<P><TABLE BGCOLOR="#E0E5F5" BORDER="0" CELLSPACING="0" CELLPADDING="5"><TR><TD><PRE>
|
|
RewriteEngine on
|
|
RewriteBase /~quux/
|
|
RewriteRule ^foo\.<STRONG>html</STRONG>$ foo.<STRONG>cgi</STRONG> [T=<STRONG>application/x-httpd-cgi</STRONG>]
|
|
</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 pure 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 served 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} <STRONG>!-s</STRONG>
|
|
RewriteRule ^page\.<STRONG>html</STRONG>$ page.<STRONG>cgi</STRONG> [T=application/x-httpd-cgi,L]
|
|
</PRE></TD></TR></TABLE>
|
|
|
|
<P>
|
|
Here a request to <CODE>page.html</CODE> leads to a internal run of a
|
|
corresponding <CODE>page.cgi</CODE> if <CODE>page.html</CODE> is still missing or has
|
|
filesize null. The trick here is that <CODE>page.cgi</CODE> is a usual CGI script
|
|
which (additionally to its STDOUT) writes its output to the file
|
|
<CODE>page.html</CODE>. Once it was run, the server sends out the data of
|
|
<CODE>page.html</CODE>. When the webmaster wants to force a refresh the contents,
|
|
he just removes <CODE>page.html</CODE> (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 <CODE>:refresh</CODE> 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 <CODE><VirtualHost></CODE> 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 <CODE>/robots.txt</CODE> 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
|
|
<CODE>/~quux/foo/arc/</CODE> (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} ^<STRONG>NameOfBadRobot</STRONG>.*
|
|
RewriteCond %{REMOTE_ADDR} ^<STRONG>123\.45\.67\.[8-9]</STRONG>$
|
|
RewriteRule ^<STRONG>/~quux/foo/arc/</STRONG>.+ - [<STRONG>F</STRONG>]
|
|
</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} <STRONG>!^$</STRONG>
|
|
RewriteCond %{HTTP_REFERER} !^http://www.quux-corp.de/~quux/.*$ [NC]
|
|
RewriteRule <STRONG>.*\.gif$</STRONG> - [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 <STRONG>^inlined-in-foo\.gif$</STRONG> - [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
|
|
<CODE>Configuration</CODE> 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} <STRONG>^badhost\.mydomain\.com$</STRONG>
|
|
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} <STRONG>^badguy@badhost\.mydomain\.com$</STRONG>
|
|
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} <STRONG>!^friend1@client1.quux-corp\.com$</STRONG>
|
|
RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <STRONG>!^friend2</STRONG>@client2.quux-corp\.com$
|
|
RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <STRONG>!^friend3</STRONG>@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 <STRONG>prg:</STRONG>/path/to/map.quux.pl
|
|
RewriteRule ^/~quux/(.*)$ /~quux/<STRONG>${quux-map:$1}</STRONG>
|
|
</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
|
|
<CODE>/~quux/foo/...</CODE> to <CODE>/~quux/bar/...</CODE>. Actually you can program
|
|
whatever you like. But notice that while such maps can be <STRONG>used</STRONG> also by
|
|
an average user, only the system administrator can <STRONG>define</STRONG> it.
|
|
|
|
</DL>
|
|
|
|
<!--#include virtual="footer.html" -->
|
|
</BLOCKQUOTE>
|
|
</BODY>
|
|
</HTML>
|