URL Rewriting mit Apache2

Vor kurzem habe ich meinen ersten Unterricht im Bereich Webserver zum Thema htaccess gehalten. Ein großes Thema dabei ist natürlich das URL Rewriting. Um die dabei gewonnenen Erkenntnisse besser festzuhalten und auch dem ein oder anderen eine Hilfe anbieten zu können wie man schönere (und suchmaschinenoptimierte) URLs bekommt möchte ich euch in diesem Artikel die Grundlagen zum URL Rewriting mit Apache2 an die Hand geben.

URL Rewriting – Was ist das?

Quasi jede Website heutzutage hat URLs wie beispielsweise http://www.mein-toller-online-shop.de/produkt/tolles-produkt-123. Aber wie funktioniert das? Wenn wir uns mit Webentwicklung beschäftigen wissen wir schließlich, dass Webseiten nichts anderes als HTML Dateien sind, diese URL deutet aber auf ein Verzeichnis hin. Natürlich könnten wir diese Verzeichnisse alle auf unserem Webserver anlegen und mit einer index.html Datei versehen (und um ehrlich zu sein dachte ich vor zehn Jahren auch, dass das genau so funktioniert). Glücklicherweise gibt es aber noch eine andere Möglichkeit.

URL Rewriting ist das Zauberwort. Unser Webserver nimmt dabei die URL und verwandelt sie nach einem von uns vorgegebenen Muster in eine andere.

Aus folgender URL:

http://www.mein-toller-online-shop.de/produkt/tolles-produkt-123

könnten wir also mit ganz wenig Aufwand diese hier machen:

http://www.mein-toller-online-shop.de/product.php?id=123

Das sieht doch schon viel sinnvoller aus. Wir laden die PHP Datei product.php und übergeben ihr die ID 123 wodurch sie weiß, welches Produkt der Besucher sehen möchte und ihm die Informationen dafür zurück geben kann.
Und das tolle daran: Auch für andere URLs kann URL Rewriting automatisch die passende Datei aufrufen und die richtigen Daten übergeben. Unsere Regel könnte zum Beispiel dafür sorgen, dass die letzte Zahl (oben wäre das die 123) als ID an eine PHP Datei übergeben wird.

Aber machen wir mal ein paar Schritte zurück und fangen am Anfang an.

 

 

.htaccess und mod_rewrite

Ein paar Vorbedingungen zur Verwendung von URL Rewriting gibt es natürlich noch.

  1. Das Apache2 Modul mod_rewrite muss installiert und aktiviert sein. Wie das funktioniert besprechen wir ein andermal in einem extra Artikel.
  2. Um die URL Rewriting Regeln in einer sogenannten .htaccess Datei festlegen zu können muss diese Möglichkeit mit der Option AllowOverride All in einer VHost Konfiguration aktiviert sein. Auch darum kümmern wir uns in einem anderen Post.

Eine .htaccess Datei ermöglicht es uns, einige Einstellungen des Apache2 Webservers zu überschreiben. Das Schöne dabei ist, wir können diese Datei in einem beliebigen Verzeichnis innerhalb unseres Webrootverzeichnisses (also dem Verzeichnis auf das unsere Domain zeigt) ablegen und sie greift nur, wenn dieses Verzeichnis teil eines Requests ist. Wir könnten also beispielsweise alle Requests individuell einstellen, die auf http://www.mein-toller-online-shop.de/kategorien zeigen aber alle unverändert lassen, die auf http://www.mein-toller-online-shop.de/impressum zeigen, indem wir eine .htaccess Datei ins Verzeichnis kategorien legen.

Beim Hantieren mit diesen .htaccess Dateien kann einiges schief gehen. Das Problem ist, wenn wir etwas kaputt machen sehen wir nicht wirklich eine Fehlermeldung sondern Apache2 wirft einfach einen Error 500. Aus diesem Grund sollten wir beim Schreiben unserer URL Rewriting Regeln auch Stück für Stück vorgehen und diese nach und nach aufbauen. Ganz wichtig hierbei: Testen. Je früher wir einen Fehler in einer Regel bemerken umso schneller finden wir die Stelle die ihn auslöst. Testen wir erst nach dreißig Zeilen Code kann es sein, dass wir diese Schritt für Schritt überprüfen müssen um einen einzelnen Fehler aufzuspüren.

Genug der Vorrede, fangen wir endlich an mit URL Rewriting!

 

 

RewriteRules

Der erste Punkt, dem wir uns widmen, sind die RewriteRules. Mit diesen Regeln legen wir fest, welche URL nach welchem Muster wie umgeschrieben werden soll. Bevor die Regeln greifen müssen wir aber Apache2 mitteilen, dass das Feature aktiviert werden soll mit dem Befehl

Dieser Befehl muss immer vor den Regeln stehen, daher werde ich ihn in den nächsten Beispielen auslassen.
Nach der RewriteEngine können wir dann unsere Regeln definieren. Eine RewriteRule hat folgendes Format:

RewriteRule MUSTER UMGESCHRIEBENE-URL [FLAGS]

  • Das MUSTER ist ein RegEx Muster, mit dem bestimmt wird, ob diese Regel für die aktuelle URL anzuwenden ist.
  • Falls das MUSTER auf die aktuelle URL zutrifft wird diese URL umgeschrieben in das, was in UMGESCHRIEBENE-URL angegeben ist.
  • Ganz am Ende können (optional!) noch Flags vergeben werden. Ein Beispiel ist die [F] Flag welche den Request direkt mit einem 403 Forbidden Status ablehnt.

Sehen wir uns diese einzelnen Teile genauer an, denn diese bilden die Basis um erfolgreiches URL Rewriting zu betreiben.

 

Das RegEx Muster

Das Muster besteht – wie in der Überschrift schon angedeutet – aus regulären Ausdrücken. Wir haben hier also die Möglichkeit, einen gewissen Teil der URL nach bestimmten Mustern zu durchsuchen um zu klären, ob diese URL neu geschrieben werden soll.
Warum ist das wichtig?

Nehmen wir eine URL wie beispielsweise /produkt/123. Hätten wir hier keine regulären Ausdrücke zur Verfügung müssten wir das URL Rewriting für jedes neue Produkt anlegen. So können wir beispielsweise das Muster ^produkt/(\d)+$ verwenden und können damit alle numerischen IDs direkt abfangen. Bei jeder Zahl hinter dem zweiten Slash würde diese Regel ab sofort greifen und erkennen Diese URL muss ich umschreiben.

 

 

Auf welchen Teil der URL bezieht sich das nun? Grundsätzlich auf alles nach der Domain. Bei der URL http://mein-toller-onlineshop.de/produkt/123 bekommen wir also für das RegEx Muster den Wert produkt/123 geliefert. Das ist wichtig da wir unsere regulären Ausdrücke natürlich genau auf diesen Wert anpassen müssen.

Eine Besonderheit sollten wir hier im Hinterkopf behalten: Verwenden wir innerhalb des Suchmusters Untergruppierungen (also mit runden Klammern abgetrennte Bereiche) können wir diese anhand ihrer Position später in der umgeschriebenen URL wieder verwenden. Die Nummerierung orientiert sich hierbei an den öffnenden Klammern.
Sehen wir uns dieses Beispiel an:

Es gibt hier insgesamt drei Gruppierungen (also drei öffnende Klammern). Diese werden nun automatisch – von links beginnend – durchnummeriert. Angefangen wir dabei bei der Zahl 1 da die 0 für den gesamten Ausdruck steht. Daraus würden sich für die einzelnen Gruppen folgende Ergebnisse ergeben:

0 – abcdef
1 – bcde
2 – c
3 – e

Das wird jetzt im nächsten Schritt wichtig.

 

Die neue URL

Im nächsten Bereich einer RewriteRule beschreiben wir dem Webserver, welche URL er aufrufen soll, wenn das Suchmuster auf die ursprüngliche URL zutrifft. Ein Beispiel wäre folgende Regel:

Wir teilen Apache2 damit also mit: Immer wenn eine URL mit produkte/ beginnt und daraufhin eine Zahl folgt, rufe intern die URL products.php?id= auf und hänge die Zahl daran an. Gar nicht so kompliziert oder?

Woher kommt jetzt die Variable $1? Wenn du dich an oben erinnerst greifen wir damit auf den Wert der ersten Untergruppe des Suchmusters – also den Wert von (\d)+ – zu.

Es ist auch noch wichtig zu wissen, dass die neu umgeschriebene URL weiter von den nächsten Regeln verarbeitet wird. Stell es dir so vor:
Oben wird eine URL in einen Aufzug gelegt, der nach unten fährt. Bei jedem Stockwerk (also jeder RewriteRule) wird überprüft, ob die URL hier verarbeitet werden soll. Ist das der Fall wird die URL aus dem Aufzug genommen, verarbeitet und die veränderte URL wieder in den Aufzug gesteckt. Dann geht es im nächsten Stockwerk weiter bis am Ende eine finale URL rauskommt, die dann aufgerufen wird.

Einen besonderen Wert an dieser Stelle nimmt der Bindestrich ein. Dieser deutet an, dass die ursprüngliche URL nicht verändert werden soll. Das macht dann hauptsächlich mit den Flags Sinn, die wir uns gleich im nächsten Abschnitt anschauen.

 

 

Flags

Als letzten Teil unserer RewriteRules gibt es noch diverse Flags. Diese verändern das Verhalten der Regel und erweitern es teilweise. Ein einfaches Beispiel ist die L(ast) Flag. Diese besagt ganz einfach: Wenn diese RewriteRule aktiv wird, brich die Verarbeitung ab und gib die veränderte URL nicht an die nächsten RewriteRules weiter. Daneben gibt es noch eine ganze Reihe weiterer Flags. Sehen wir uns ein paar davon an:

  • F(orbidden) – Brich die Verarbeitung ab und gib dem Browser eine Statusmeldung von 403 – Forbidden zurück.
  • NC(No Case) – Sorgt dafür, dass das Muster nicht auf Groß- und Kleinschreibung achtet, entspricht also dem RegEx i Modifier.
  • QSA(Query String Append) – Sollte die URL noch Query Daten angehängt haben (beispielsweise /produkt/123?order=asc) können diese im Suchmuster nicht abgefragt werden. Sollte ich in der Ergebnisurl aber Query Daten anhängen gingen diese verloren. Die QSA Flag sorgt dafür, dass die angehängten Query Daten auch automatisch an die neue URL angehängt werden, was gerade für PHP natürlich sehr wichtig sein kann.
  • R(edirect) – Normalerweise kümmert Apache2 sich darum, dass die neue URL aufgerufen wird. Mit der R Flag können wir diese Aufgabe aber dem Browser übergeben indem wir ihm eine Response mit Redirect Header zurücksenden.

Natürlich gibt es noch einige andere Flags aber für eine Einführung sollte das genügen. Widmen wir uns lieber einem weiteren größeren Thema, das noch ansteht.

 

RewriteConditions

Neben den RewriteRules gibt es beim URL Rewriting auch noch RewriteConditions (abgekürzt mit dem Befehl RewriteCond). Diese stellen Bedingungen dar, die sich immer genau auf eine RewriteRule (nämlich die am nächsten folgende) beziehen und dafür sorgen, dass diese RewriteRule nur dann ausgeführt wird, wenn alle dazu gehörigen Bedingungen erfüllt sind.

Übertragen wir das ganze auf unser Aufzugbeispiel von oben können wir uns das ganze wie Türsteher vorstellen. Auf jedem einzelnen Stockwerk hält der Aufzug an und wenn das Suchmuster auf die URL zutrifft geht die Tür auf. Vor der Tür stehen nun beliebig viele Türsteher – die RewriteConditions. Und jeder einzelne Türsteher entscheidet selbst, ob die URL aussteigen darf oder nicht. Nur wenn alle ihr Okay geben kann die URL auf diesem Stockwerk aussteigen, verändert werden und wieder in den Aufzug steigen. Andernfalls geht die Tür wieder zu und fährt ein Stockwerk weiter.

 

 

Die Struktur einer Bedingung sieht dabei relativ simpel aus:

RewriteCond VARIABLE BEDINGUNG [FLAGS]

Auch hier gehen wir die einzelnen Bestandteile natürlich wieder Schritt für Schritt durch.

Die Variable

Als Variable in einer Bedingung haben wir jede Menge Werte zur Auswahl. Zum Teil sind das Servervariablen wie zum Beispiel den HTTP_REFERER oder den REQUEST_FILENAME aber auch besondere Variablen wie zum Beispiel THE_REQUEST stehen hier zur Verfügung.

Um die Variable anzusprechen packen wir sie einfach in folgendes Gerüst: %{}, für den aufgerufenen Dateinamen der aktuellen URL könnten wir also %{REQUEST_FILENAME} verwenden.

 

Die Bedingung

Im zweiten Teil einer RewriteCond können wir nun angeben, welche Bedingung auf den Wert unserer Variable zutreffen muss. Hier können wir als Beispiel -f verwenden, welches einfach bedeutet: Der angegebene Variablenwert ist eine existierende Datei auf dem Server.

Natürlich gibt es noch mehr Bedingungen. Hier ein paar Beispiele:

  • -d überprüft, ob ein Verzeichnis (directory) an diesem Pfad existiert
  • -l überprüft, ob ein Symlink (link) an diesem Pfad existiert
  • -x überprüft, ob der User ausführende Rechte für den Pfad besitzt

Diese Bedingungen können wir natürlich auch umdrehen indem wir ein Ausrufezeichen vorne anstellen. Die Bedingung !-d würde also bedeuten: Es existiert KEIN Verzeichnis unter dem angegebenen Pfad.

Wofür ist sowas nun sinnvoll? Nun nehmen wir das Beispiel Bilder. Wir überprüfen anhand einer Bedingung, ob ein angefordertes Bild wirklich physisch auf dem Server existiert. Existiert diese Datei nicht rufen wir beispielsweise ein PHP Script auf, das einen Fehler ausgibt.

 

 

Flags

Genau wie oben gibt es auch hier nochmal Flags, die wir setzen können um das Verhalten zu verändern. Einerseits wäre da die bekannte NC Flag, die einfach dafür sorgt, dass Groß- und Kleinschreibung nicht mehr unterschieden wird. Andererseits gibt es noch die OR Flag, die dafür sorgt, dass zwei aufeinander folgende RewriteConds mit einem logischen ODER anstatt eines logischen UNDs verknüpft werden. Das bedeutet ganz einfach, dass nur noch eine der beiden Bedingungen erfüllt sein muss, damit die Regel ausgeführt wird.

 

 

Das war jetzt mal ein – gar nicht so kleiner – Überblick über die Funktionalität hinter URL Rewriting. Ich hoffe, dieser Artikel hat dir geholfen. Hast du noch Fragen, Tipps oder Anmerkungen zu diesem Thema? Dann hinterlass mir doch gerne einen Kommentar unter dem Artikel.

2 Kommentare zu “URL Rewriting mit Apache2

  1. Hallo Frank,

    mal wieder sehr gut erklärt, vielen Dank!
    Es ist das erste Mal, dass ich es wirklich verstanden habe und auch anwenden konnte.
    Bei Tutorials von anderen Autoren habe ich bisher immer aufgegeben. Grundsätzlich bin
    ich also nicht zu dumm für solche Sachen, es ist eben doch nicht egal wie man es erklärt
    bekommt.

    Ich hoffe du setzt deine hervorrangende Reihe fort. Falls du noch ein Thema für die Zukunft
    suchst, dann würde ich mich riesig über einen Artikel freuen, der sich mit Validierung und
    Säuberung von Formulardaten befasst. Das Thema finde ich auch ziemlich schwierig dabei
    ist es doch so wichtig.

    Gruß
    René

    • Hi René,

      vielen Dank für das Lob, es freut mich immer wenn ich jemandem helfen kann.
      Deine Aussage mit „immer aufgegeben“ versteh ich auch sehr gut, ging mir auch immer so. Deshalb schreib ich auch hier die Artikel gerne auf, da ich finde, man hat etwas erst verstanden wenn man es auch erklären kann.

      Validierung ist ein großes Thema, da hast du absolut recht. Aktuell verlass ich mich da meistens sehr auf Laravel, da dort die Validierung meiner Meinung nach super gelöst ist mit den FormRequests.

      Viele Grüße
      Frank

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*