Self-Hosted

Apache RewriteRule auslagern: Weiterleitungen sauber organisieren

Viele Rewrite-Regeln machen eine Apache-Konfiguration schnell unübersichtlich. Dieser Beitrag zeigt, wie sich RewriteRule und RewriteCond mit Include in externe Dateien auslagern, sinnvoll strukturieren und sicher testen lassen.

7 min Lesezeit
Grafik: Apache RewriteRule in externe Datei auslagern

Wer einen Apache-Webserver über längere Zeit betreibt, kennt das Problem: Aus einer ursprünglich übersichtlichen VirtualHost-Konfiguration wird nach und nach eine umfangreiche Sammlung aus Weiterleitungen, Rewrite-Regeln und Sonderfällen.

Ein paar RewriteRule-Anweisungen sind noch problemlos überschaubar. Werden daraus jedoch Dutzende oder sogar Hunderte Weiterleitungen, leidet die Übersichtlichkeit erheblich.

Apache bietet dafür eine einfache Lösung: Rewrite-Regeln können in separate Konfigurationsdateien ausgelagert und anschließend mit Include oder IncludeOptional eingebunden werden.

Das macht die Apache-Konfiguration übersichtlicher und erleichtert insbesondere die Pflege größerer Redirect-Sammlungen.

Warum Rewrite-Regeln auslagern?

Eine typische Apache-Konfiguration könnte zunächst so aussehen:

<VirtualHost *:443>
    ServerName example.de
    DocumentRoot /var/www/example

    RewriteEngine On

    RewriteRule ^/alte-seite$ /neue-seite [R=301,L]
    RewriteRule ^/alter-bereich/(.*)$ /neuer-bereich/$1 [R=301,L]
    RewriteRule ^/produkte-alt/(.*)$ /produkte/$1 [R=301,L]

    # zahlreiche weitere Regeln
</VirtualHost>

Bei drei Regeln ist das noch kein Problem. Bei hundert oder mehreren hundert Weiterleitungen wird die VirtualHost-Datei dagegen schnell unübersichtlich.

Eine bessere Struktur wäre beispielsweise:

/etc/apache2/
├── apache2.conf
├── sites-available/
│   └── example.conf
└── rewrites/
    ├── redirects.conf
    ├── legacy.conf
    └── special.conf

Die eigentliche VirtualHost-Konfiguration enthält anschließend nur noch die notwendigen Includes.

Externe Rewrite-Datei erstellen

Zunächst wird beispielsweise ein eigenes Verzeichnis angelegt:

mkdir /etc/apache2/rewrites

Anschließend erstellen wir:

/etc/apache2/rewrites/example.conf

Darin können die Rewrite-Regeln stehen:

RewriteRule ^/alte-seite$ /neue-seite [R=301,L]

RewriteRule ^/alter-bereich/(.*)$ /neuer-bereich/$1 [R=301,L]

RewriteRule ^/produkte-alt/(.*)$ /produkte/$1 [R=301,L]

Die Datei ist keine eigenständige Apache-Konfiguration. Ihr Inhalt wird vielmehr an der Stelle verarbeitet, an der die Datei später eingebunden wird.

Genau dieser Punkt ist wichtig.

Datei mit Include einbinden

Innerhalb des VirtualHosts lässt sich die Datei folgendermaßen einbinden:

<VirtualHost *:443>
    ServerName example.de
    DocumentRoot /var/www/example

    RewriteEngine On

    Include /etc/apache2/rewrites/example.conf
</VirtualHost>

Apache verarbeitet dies sinngemäß so, als stünden die Rewrite-Regeln unmittelbar an der Stelle der Include-Anweisung.

Das bedeutet auch:

Der Kontext des Includes bestimmt den Kontext der darin enthaltenen Apache-Direktiven.

Wird die Datei innerhalb eines VirtualHosts eingebunden, befinden sich die darin enthaltenen Rewrite-Regeln ebenfalls im VirtualHost-Kontext.

RewriteEngine nicht vergessen

Damit mod_rewrite die Regeln verarbeitet, muss die Rewrite Engine aktiviert sein:

RewriteEngine On

Das kann beispielsweise vor dem Include geschehen:

RewriteEngine On
Include /etc/apache2/rewrites/example.conf

Alternativ könnte RewriteEngine On auch Bestandteil der externen Datei sein.

Ich bevorzuge bei größeren Konfigurationen allerdings die erste Variante. Dadurch ist bereits im VirtualHost unmittelbar erkennbar, dass Rewrite-Regeln verwendet werden.

RewriteRule im VirtualHost und in .htaccess unterscheiden sich

Ein häufiger Stolperstein beim Verschieben von Rewrite-Regeln ist der unterschiedliche Kontext.

Eine Regel aus einer .htaccess-Datei lässt sich nicht zwangsläufig unverändert in einen VirtualHost übernehmen.

In einer .htaccess könnte beispielsweise stehen:

RewriteRule ^alte-seite$ /neue-seite [R=301,L]

Im Server- beziehungsweise VirtualHost-Kontext kann dagegen der führende Slash relevant sein:

RewriteRule ^/alte-seite$ /neue-seite [R=301,L]

Wer vorhandene .htaccess-Regeln in eine zentrale Apache-Konfiguration verschiebt, sollte daher nicht einfach sämtliche Regeln kopieren, sondern deren Kontext und Matching-Verhalten überprüfen.

RewriteCond ebenfalls auslagern

Natürlich können nicht nur RewriteRule, sondern auch die dazugehörigen Bedingungen ausgelagert werden.

Beispiel:

RewriteCond %{HTTP_HOST} ^www\.example\.de$ [NC]
RewriteRule ^/(.*)$ https://example.de/$1 [R=301,L]

Dabei muss darauf geachtet werden, dass zusammengehörige Bedingungen und Regeln nicht versehentlich voneinander getrennt werden.

Eine RewriteCond bezieht sich grundsätzlich auf die unmittelbar folgende RewriteRule.

Aus:

RewriteCond %{HTTP_HOST} ^www\.example\.de$ [NC]
RewriteRule ^/(.*)$ https://example.de/$1 [R=301,L]

sollte deshalb nicht plötzlich eine über mehrere Dateien verteilte Konstruktion werden.

Include oder IncludeOptional?

Apache bietet unter anderem zwei interessante Möglichkeiten:

Include /etc/apache2/rewrites/example.conf

und:

IncludeOptional /etc/apache2/rewrites/example.conf

Der Unterschied ist insbesondere dann relevant, wenn eine Datei möglicherweise nicht vorhanden ist.

Bei Include wird erwartet, dass die angegebene Datei beziehungsweise das angegebene Muster vorhanden ist. Ein Fehler kann verhindern, dass Apache seine Konfiguration korrekt lädt.

IncludeOptional ist toleranter, wenn keine passende Datei existiert.

Das kann beispielsweise für optionale Konfigurationsfragmente praktisch sein:

IncludeOptional /etc/apache2/rewrites/*.conf

Damit lassen sich alle entsprechenden Dateien eines Verzeichnisses einbinden.

Rewrite-Regeln thematisch organisieren

Bei größeren Webservern kann eine weitere Aufteilung sinnvoll sein.

Beispielsweise:

/etc/apache2/rewrites/
├── redirects.conf
├── legacy.conf
├── domains.conf
├── migrations.conf
└── special.conf

Die VirtualHost-Konfiguration könnte anschließend so aussehen:

RewriteEngine On

Include /etc/apache2/rewrites/domains.conf
Include /etc/apache2/rewrites/legacy.conf
Include /etc/apache2/rewrites/redirects.conf
Include /etc/apache2/rewrites/special.conf

Damit wird bereits anhand der Dateinamen deutlich, wofür die jeweiligen Regeln zuständig sind.

Das ist vor allem bei Webseiten hilfreich, die mehrfach umgebaut oder auf andere CMS-Systeme migriert wurden.

Vorsicht bei Wildcards und Reihenfolge

Sehr komfortabel erscheint zunächst:

IncludeOptional /etc/apache2/rewrites/*.conf

Das hat allerdings einen Nachteil: Bei Rewrite-Regeln ist die Reihenfolge häufig von Bedeutung.

Betrachten wir beispielsweise:

RewriteRule ^/blog/(.*)$ /archiv/$1 [R=301,L]

und eine speziellere Regel:

RewriteRule ^/blog/linux$ /linux-tutorials/ [R=301,L]

Je nachdem, welche Regel zuerst ausgewertet wird, kann die allgemeine Regel bereits greifen und durch das Flag [L] die weitere Rewrite-Verarbeitung beenden.

Bei vielen Rewrite-Dateien bevorzuge ich deshalb explizite Includes:

Include /etc/apache2/rewrites/01-domains.conf
Include /etc/apache2/rewrites/10-special.conf
Include /etc/apache2/rewrites/20-legacy.conf
Include /etc/apache2/rewrites/30-redirects.conf

Alternativ können Dateinamen mit Nummern versehen werden, wenn bewusst mit einem Wildcard-Include gearbeitet wird.

So ist die gewünschte Reihenfolge wesentlich leichter nachvollziehbar.

Permanente und temporäre Weiterleitungen

Beim Auslagern lohnt sich gleichzeitig ein Blick auf die verwendeten HTTP-Statuscodes.

Ein permanenter Redirect wird häufig mit:

[R=301,L]

realisiert.

Beispiel:

RewriteRule ^/alte-seite$ /neue-seite [R=301,L]

Für eine vorübergehende Weiterleitung kann dagegen beispielsweise ein temporärer Redirect verwendet werden:

RewriteRule ^/wartung$ /status/ [R=302,L]

Gerade bei Tests sollte man mit permanenten Weiterleitungen vorsichtig sein, da Browser und andere Clients diese zwischenspeichern können.

Wann Redirect statt RewriteRule sinnvoll ist

Nicht jede einfache Weiterleitung benötigt mod_rewrite.

Für einen simplen statischen Redirect kann beispielsweise auch Redirect aus mod_alias verwendet werden:

Redirect permanent /alte-seite https://example.de/neue-seite

mod_rewrite spielt seine Stärke insbesondere dann aus, wenn reguläre Ausdrücke, Bedingungen oder komplexere Entscheidungen erforderlich sind.

Beispielsweise:

RewriteCond %{QUERY_STRING} (^|&)id=([0-9]+)(&|$)
RewriteRule ^/old\.php$ /article/%2? [R=301,L]

Bei einer großen Redirect-Sammlung lohnt es sich deshalb durchaus zu prüfen, welche Regeln tatsächlich die Möglichkeiten von mod_rewrite benötigen.

Apache-Konfiguration vor dem Reload prüfen

Nach Änderungen an Apache-Konfigurationsdateien sollte niemals sofort ein unkontrollierter Neustart durchgeführt werden.

Unter Debian und Ubuntu kann zunächst die Konfiguration geprüft werden:

apache2ctl configtest

Alternativ steht häufig auch zur Verfügung:

apachectl configtest

Ist alles in Ordnung, erscheint:

Syntax OK

Erst anschließend sollte die Konfiguration neu geladen werden:

systemctl reload apache2

Der Reload hat gegenüber einem vollständigen Neustart den Vorteil, dass die Konfiguration neu eingelesen wird, ohne den Webserver unnötig hart zu unterbrechen.

Fehler in ausgelagerten Rewrite-Regeln finden

Funktioniert eine Regel nicht wie erwartet, sollte zunächst überprüft werden, ob die externe Datei tatsächlich eingebunden wird.

Hilfreich ist:

apache2ctl -S

Damit lässt sich unter anderem die VirtualHost-Konfiguration kontrollieren.

Auch das Apache-Error-Log sollte bei Problemen geprüft werden:

tail -f /var/log/apache2/error.log

Für komplizierte Rewrite-Probleme kann außerdem das Logging von mod_rewrite über LogLevel detaillierter eingestellt werden.

Beispielsweise temporär:

LogLevel warn rewrite:trace3

Bei Bedarf kann der Trace-Level weiter erhöht werden. Ein sehr ausführliches Rewrite-Logging sollte auf produktiven Systemen jedoch nur gezielt zur Fehlersuche eingesetzt werden, da schnell große Mengen an Logdaten entstehen können.

Beispiel für einen aufgeräumten VirtualHost

Eine umfangreiche Konfiguration kann am Ende erstaunlich übersichtlich aussehen:

<VirtualHost *:443>

    ServerName example.de
    ServerAlias www.example.de

    DocumentRoot /var/www/example

    RewriteEngine On

    Include /etc/apache2/rewrites/example-domains.conf
    Include /etc/apache2/rewrites/example-special.conf
    Include /etc/apache2/rewrites/example-legacy.conf
    Include /etc/apache2/rewrites/example-redirects.conf

    ErrorLog ${APACHE_LOG_DIR}/example-error.log
    CustomLog ${APACHE_LOG_DIR}/example-access.log combined

</VirtualHost>

Die eigentliche VirtualHost-Datei beschreibt damit wieder hauptsächlich den VirtualHost.

Hunderte Zeilen historisch gewachsener Weiterleitungen befinden sich dagegen in den dafür vorgesehenen Dateien.

Änderungen testen

Gerade bei umfangreichen Redirect-Listen sollte nicht nur die Apache-Syntax geprüft werden.

Ein

apache2ctl configtest

stellt lediglich fest, ob Apache die Konfiguration grundsätzlich versteht. Es sagt nicht, ob eine Weiterleitung fachlich das gewünschte Ergebnis liefert.

Hier kann beispielsweise curl helfen:

curl -I https://example.de/alte-seite

Die Antwort könnte unter anderem enthalten:

HTTP/2 301
location: https://example.de/neue-seite

Damit lässt sich schnell überprüfen, ob der gewünschte Statuscode und das richtige Ziel zurückgegeben werden.

Bei komplexeren Migrationen empfiehlt es sich sogar, eine Liste wichtiger alter URLs automatisiert gegen die neue Konfiguration zu testen.


Rewrite-Regeln müssen keineswegs direkt in einer großen apache.conf oder VirtualHost-Datei stehen. Mit Include beziehungsweise IncludeOptional lassen sich die Regeln sauber in separate Dateien auslagern.

Gerade bei umfangreichen Webseiten ist das eine sehr sinnvolle Methode, um die Apache-Konfiguration langfristig wartbar zu halten.

Besonders wichtig sind dabei drei Punkte: Kontext, Reihenfolge und Tests.

Eine ausgelagerte Datei wird im Kontext der Stelle verarbeitet, an der sie eingebunden wurde. Die Reihenfolge verschiedener Rewrite-Regeln kann das Ergebnis entscheidend beeinflussen. Und nach jeder Änderung sollten sowohl die Apache-Konfiguration als auch die tatsächlichen Redirects überprüft werden.

Wer diese Punkte berücksichtigt, kann selbst umfangreiche Sammlungen aus Weiterleitungen sauber organisieren – ohne dass der eigentliche VirtualHost irgendwann aus mehreren hundert Zeilen RewriteRule besteht.