mod_rewrite


1. Was und warum?
2. Installation
3. RewriteRule
4. RewriteCond
5. RewriteMap
6. Anhang

1. Was und warum?

mod_rewrite ist ein Apachemodul, wurde 1996 von Ralf Engelschall entwickelt und ging 1997 an die Apache Group über.
Mit Hilfe dieses Moduls ist es möglich, URL-Anfragen abzufragen und zu verändern. Diese Manipulation kann dazu verwendet werden, unschöne URLs vor Usern und Suchmaschinen zu verstecken, varialbe Redirects einzurichten, verschiedene Robots auszusperren und vieles mehr. Die Umwandlung der URL ist nur intern, so dass der Browser (oder Bot) in der Regel nichts von der Änderung mitbekommt.

2. Installation

Um mod_rewrite zu aktivieren benötigt man Zugriff auf die httpd.conf von Apache. In der httpd.conf selbst ist nicht viel zu tun. Es gilt nur, die beiden Einträge rewrite_module modules/mod_rewrite.so und AddModule mod_rewrite.c zu finden. Wenn diese beiden Zeilen auskommentiert sind, müssen nur die # davor entfernt werden. Dann noch die Datei speichern und den Apache neu starten.
Die RewriteRegeln selbst können später entweder direkt in die httpd.conf oder in .htaccess Dateien eingetragen werden. Da man in der Regel bei Webspace keinen Zugriff auf die httpd.conf hat, werde ich im Weiteren von der Verwendung von .htaccess ausgehen.

3. RewriteRule

Es gibt zwar noch einige andere Direktiven, aber RewriteRule ist das eigentliche Kernstück. Sie folgt der Syntax "RewriteRule Muster Ersetzung [flags]". Bei Muster handelt es sich um einen regular expression. Die ganze Regel ist so zu verstehen, dass jede URL, die dem regular expression Muster entspricht, durch Ersetzung ersetzt wird. Flags sind optional. Sie können dazu verwendet werden, das Verhalten beim Ersetzen der URL zu beeinflussen.

Nehmen wir jetzt in unserem ersten Beispiel an wir haben die Datei xxx.html im Verzeichnis mydir in yyy.html umbenannt. Nun wollen wir aber nicht, dass die URL http://myhost.com/mydir/xxx.htlm ihre Gültigkeit verliert oder dass überhaupt jemand bemerkt, dass sich der Name geändert hat. Deshalb legen wir nun eine .htaccess Datei an, die folgenden Inhalt hat:

Listing 3.1.a
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^mydir/xxx\.html$ mydir/yyy.htlm
</IfModule>

Die Klammerung mit <IfModule mod_rewrite.c> und </IfModule> bedeutet nur, dass das was innerhalb der Tags steht nur beachtet wird, falls das Modul mod_rewrite.c geladen wurde.
Die erste Zeile die uns wirklich interessiert ist RewriteEngine On. Da mod_rewrite in der Standarteinstellung deaktiviert ist, muss es erst mit diesem Befehl aktiviert werden. Diese Direktive kann man zum Beispiel nutzen, um sich das Auskommentieren aller Regeln zu ersparen.
Die dritte Zeile ist nun endlich die erste wirkliche RewriteRule. Es ist eine sehr strenge Regel, die wirklich nur auf http://myhost.com/mydir/xxx.htm anspringt, aber genau das war ja das Ziel.
Nehmen wir jetzt als nächstest an, wir hätten lieber eine Weiterleitung auf yyy.htm statt einer "Maskierung". Dazu fügen wir einfach an die RewriteRule ein [R] (Redirect) an, so dass die ganze Regel so aussieht.

Listing 3.1.b
RewriteRule ^mydir/xxx\.html$ mydir/yyy.html [R]

Ein weiterer wichtiger Bestandteil von RewriteRule sind die so genannten back-references. Sie ermöglichen es Teile der ursprünglichen URL (oder auch die ganze URL) in die Neue zu übernehmen. Dazu ein Beispiel: Anfragen an html Dateien sollen auf php Dateien übertragen werden. Also index.html -> index.php aber auch foo.html?task=submit -> foo.php?task=submit, ohne das wir anfangen html zu parsen. Dazu folgende Regel.

Listing 3.2
RewriteRule ^(.*)\.html(.*)$ $1.php$2

Hier sehen wir jetzt zum ersten mal die Verwendung von back-references. Mit Hilfe von normalen Klammern lassen sich Teile des regular expression gruppieren. Sie stehen dann im Ersetzungsteil als $1 ... $n zur Verfügung. Dabei gilt, $1 = Inhalt der ersten Klammer, $2 = Inhalt der zweiten Klammer, usw. .
Als nächstes werden wir unsere beiden Regeln miteinander kombinieren bzw. sie einfach hintereinander schalten.

Listing 3.3
RewriteRule ^mydir/xxx\.htm$ mydir/yyy.htm
RewriteRule ^(.*)\.html(.*)$ $1.php$2

Falls nicht anders angegeben (z.B. durch [L]), werden alle Regeln nacheinander auf eine URL angewendet. In unserem Beispiel bedeutet das, dass ein Aufruf von xxx.html zunächst in yyy.html übersetzt wird. Danach wird die nächste Regel angewandt, was dazu führt, dass die neue URL yyy.html nach yyy.php übersetzt wird.
Vielleicht ist inzwischen aufgefallen, dass wir uns hier ein kleines Problem gebastelt haben. Es ist zur Zeit nicht mehr möglich mit diesen Regeln html Dateien aufzurufen. Darauf werden wir später in Verbindung mit RewriteCond eingehen.

4. RewriteCond

Einer RewriteRule können eine oder mehrere RewriteConds vorangestellt werden. RewriteCond ist vergleichbar mit einer if-Abfrage. Wenn die Abfrage erfüllt ist, wird die nächste ihr folgende RewriteRule ausgeführt. Falls nicht, wird die RewriteRule übersprungen. RewriteCond hat die Syntax "RewriteCond TestString Muster [flags]". Bei Muster handelt es sich entweder um einen regular expression oder um einen speziellen String. Ausserdem kann Muster noch ein "!" voran gestellt werden, was zur Negation führt. RewriteCond ist erfüllt, wenn Muster und TestString übereinstimmen.

Und nun zum Problem von Listing 3.2 bzw. 3.3 . Zunächst sollten wir uns überlegen, welche Fälle auftreten können, wie sie im Moment behandelt werden und wie sie behandelt werden sollen.
Beschreibung Derzeitige Bearbeitung Gewünschte Bearbeitung
Fall1: Es existiert weder eine passende Datei mit der Endung .html noch eine mit der Endung .php Dieser Fall soll uns nicht weiter interessieren, der User hat eine ungültige URL übermittelt und bekommt einen netten 404-Fehler dafür.
Fall2: Es existiert nur eine passende html Datei. Der User erhält einen 404-Fehler. Die html-Datei soll übermittelt werden.
Fall3: Es existiert nur eine passende php Datei Die php-Datei wird übermittelt. Soll gleich bleiben.
Fall4: Es existieren sowohl eine passende html, als auch eine passene php Datei. Die php-Datei wird übermittelt. Die html Datei soll übermittelt werden.

Dies lässt sich durch eine RewriteCond realisieren.

Listing 4.1
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)\.html(.*)$ $1.php$2

RewriteCond überprüft, ob die angeforderte Datei (%{REQUEST_FILENAME}) existiert (-f). Die RewriteCond ist erfüllt, falls die Datei nicht existiert (!). Konkreter, es wird nur eine php Datei übermittelt, wenn keine passende html Datei existiert. In diesem Beispiel wird kein regulärer Ausdruck benutzt sondern ein spezieller String. Von dieser Art String gibt es noch andere (z.B. -d, welcher überprüft, ob es sich um ein Verzeichnis handelt). Eine komplette Liste kann der Dokumentation entnommen werden ebenso wie eine Liste der Umgebungsvariablen (z.B. REQUEST_FILENAME, HTTP_USER_AGENT, usw.).
Als nächstes wollen wir verhindern, dass irgendwelche Bots über unsere Seiten huschen und E-Mailadressen sammeln. Dazu folgende Regeln.

Listing 4.2
...
RewriteCond %{HTTP_USER_AGENT} ^EmailCollector [OR]
RewriteCond %{HTTP_USER_AGENT} ^EmailSiphon [OR]
RewriteCond %{HTTP_USER_AGENT} ^EmailWolf [OR]
...
RewriteRule ^.* - [F,L]

Alle Bedingungen, die zu unserer RewriteRule führen sind untereinander aufgelistet und mit dem [OR]-Flag versehen. Das bedeutet, dass sobald eine der RewriteConds erfüllt (also true) ist, wird die nächste RewriteRule ausgeführt. Wenn die [OR]-Flags nicht gesetzt wären, würden die RewriteConds statt mit einem logischen Oder, mit einem logischen Und verknüpft. Das hätte in unserem Beispiel dazu geführt, dass die RewriteRule nie ausgeführt worden wäre.
Was vielleicht noch interessant ist, sind die Flags der RewriteRule. F (forbidden) führt dazu, dass der Code 403 zurück gesendet wird. L (last) hat zur Folge, dass nach dieser Regel keine weitere mehr angewandt wird. Eine komplette Liste der Flags kann der Dokumentation entnommen werden.
Durch %1 ... %n stehen ausserdem noch die gruppierten Bereiche von Muster der letzten zutreffenden RewriteCond zur Verfügung.

5. RewriteMap

Rewrite Maps sind eine weitere praktische Funktion von mod_rewrite. Sie erlauben es, Rewriting Maps anzulegen die von RewriteRule und RewriteCond verwendet werden können, um den zu einem bestimmen Schlüssel gehörenden Wert einzufügen. Die Deklaration hat die Syntax RewriteMap Mapname Maptype:MapSource. Die Verwendung der Map hingegen folgt der Syntax ${MapName : LookupKey|DefaultValue} bzw. ${MapName : LookupKey|DefaultValue}. Allerdings können diese Regeln zwar in .htaccess genutzt, aber nur per-server (also nur in Apaches Konfigurationsdateien) deklariert werden. Trotzdem können wir uns an dem folgenden Beispiel angucken, wie das Ganze funktioniert.

Nehmen wir in diesem Beispiel an, wir wollen verschiedenen IPs den Zugriff auf unsere Seiten verbieten. Dazu legen wir neben einigen Regeln noch die Datei badhosts.txt an.

Listing 5.1.a Listing 5.1.b (badhosts.txt)
RewriteMap bad-hosts txt:/path/to/badhosts.txt
RewriteCond ${bad-hosts:%{REMOTE_ADDR}|empty} !=empty
RewriteRule ^/.* - [F,L]
...
198.168.55.85  -
192.168.145.79 -
210.33.123.111 -
...

Was passiert nun? Als erstes deklarieren wir eine RewriteMap mit dem Namen "bad-hosts", dem Typ "txt" die zu finden ist unter "/path/to/badhosts.txt". Als nächstes kommt eine RewriteCond zum Einsatz, die Gebrauch von der gerade deklarierten RewriteMap macht. Konkret schaut sie in badhosts.txt nach, ob der Schlüssesl %{REMOTE_ADDR} (also die IP des Clients, der versucht auf die Seite zuzugreifen) existiert und liefert gegebenenfalls den entsprechenden Wert (in unserer badhosts.txt wird jedem Wert der Schlüssel "-" zugewiesen) zurück. Falls kein entsprechender Schlüssel existiert, wird "empty" verwendet. Die RewriteCond ist jetzt so zu verstehen, dass wenn die Client-IP als Schlüssel in badhosts.txt (sozusagen unsere "Schwarze List") der Zugriff verweigert wird.

6. Anhang

So zum Schluss noch ein paar(hoffentlich) nützliche Links.

Die ausführliche Dokumentation
http://httpd.apache.org/docs/mod/mod_rewrite.html

Sehr viele Beispiele, wozu mand mod_rewrite nutzen kann
http://httpd.apache.org/docs/misc/rewriteguide.html

Forum rund um mod_rewrite
http://forum.modrewrite.com/

Für viele weitere Informationen über mod_rewrite und jedes andere Thema ;)
http://google.de/

Falls Fehler in den Listings oder den Erklärungen auftauchen, würd ich mich über eine Mail freuen.