.htaccess: Der vollständige Praxisleitfaden
Wie die .htaccess aufgebaut ist, in welcher Reihenfolge Apache die Direktiven auswertet und welche Regeln in der Praxis Ärger machen - mit Beispielen zum Übernehmen.
Das Wichtigste
- Die
.htaccesswirkt pro Verzeichnis und vererbt sich nach unten. Eine Datei im Unterordner ersetzt nicht die darüberliegende, sie ergänzt sie. - Apache liest die Datei bei jedem Request neu. Auf eigenen Servern ist
AllowOverride Noneplus Regeln im VirtualHost schneller. - Was in der
.htaccesserlaubt ist, entscheidetAllowOverride. Ein500nach dem Speichern heißt meist: Direktive nicht freigegeben, nicht falsch geschrieben. - Reihenfolge zählt.
RewriteRule-Blöcke werden von oben nach unten abgearbeitet,[L]beendet nur den aktuellen Durchlauf, nicht die Verarbeitung.
Die .htaccess ist eine Konfigurationsdatei, die pro Verzeichnis gilt und ohne Serverneustart wirkt. Genau das macht sie auf Shared Hosting unverzichtbar: Du hast keinen Zugriff auf die httpd.conf, aber du darfst in deinem Webspace Regeln setzen. Und genau das macht sie gefährlich, weil ein Tippfehler sofort produktiv wirkt.
Wie Apache die Datei auswertet
Bei jeder Anfrage geht Apache den Pfad vom Dokumentwurzelverzeichnis bis zum Zielverzeichnis durch und sammelt unterwegs jede .htaccess ein. Die Direktiven werden dabei nicht ersetzt, sondern gestapelt: Setzt die Datei ganz oben Options -Indexes und die im Unterordner Options +FollowSymLinks, gilt im Unterordner nur noch das zweite, weil Options ohne Vorzeichen den kompletten Satz ersetzt. Mit Vorzeichen (+/-) wird ergänzt.
Ob eine Direktive überhaupt erlaubt ist, entscheidet AllowOverride in der Serverkonfiguration. Der häufigste Support-Fall lautet: Regel ist korrekt, Modul ist geladen, trotzdem 500. Dann steht in der Serverkonfiguration AllowOverride auf einem Wert, der die Gruppe nicht einschließt.
| AllowOverride | Erlaubt unter anderem |
|---|---|
All | alles, was der Hoster nicht separat sperrt |
FileInfo | RewriteRule, Redirect, ErrorDocument, AddType |
AuthConfig | AuthType, AuthUserFile, Require |
Limit | Require ip, Require all denied |
Indexes | DirectoryIndex, Options +Indexes |
None | nichts, die Datei wird gar nicht gelesen |
Ein Grundgerüst, das nichts kaputtmacht
Diese Basis lässt sich auf fast jedem Apache- oder LiteSpeed-Hosting übernehmen:
# Verzeichnislisting abschalten
Options -Indexes
# Standard-Startdatei
DirectoryIndex index.html index.php
# Zeichensatz, damit Umlaute nicht vom Serverdefault abhaengen
AddDefaultCharset UTF-8
# Versteckte Dateien und Backups nicht ausliefern
<FilesMatch "^\.|~$|\.(bak|sql|log|ini|env)$">
Require all denied
</FilesMatch>
# Eigene Fehlerseiten
ErrorDocument 404 /404/
ErrorDocument 403 /404/
Der FilesMatch-Block ist der Teil, der am häufigsten fehlt. Eine vergessene datenbank.sql im Webroot ist ohne diese Regel öffentlich abrufbar, und Scanner suchen genau danach.
HTTPS erzwingen, ohne die Domainvariante zu verlieren
Der üblich kopierte HTTPS-Block sieht so aus:
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Das erzwingt HTTPS, kanonisiert aber nicht zwischen www und Apex, weil %{HTTP_HOST} den angefragten Host unverändert übernimmt. Wer beide Varianten mit 200 ausliefert, hat denselben Inhalt unter zwei Adressen im Index. Dafür braucht es eine zweite Regel:
# Apex ist kanonisch: www wird umgeleitet
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L,NE]
Das Ziel muss absolut mit Schema beginnen. Ein als protokollrelativ gedachtes //example.com/$1 ist in einer RewriteRule-Substitution kein URL, sondern ein lokaler Pfad. Apache hängt ihn dann an den bestehenden Pfad an, der Host bleibt www, und jeder Durchlauf verlängert die URL weiter, bis der Browser mit ERR_TOO_MANY_REDIRECTS aufgibt.
Das NE verhindert, dass bereits kodierte Zeichen im Pfad ein zweites Mal kodiert werden. Ohne NE wird aus %20 ein %2520.
Caching und Kompression
Zwei Blöcke, die auf Ausliefergeschwindigkeit spürbar wirken:
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css text/javascript \
application/javascript application/json image/svg+xml
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresDefault "access plus 2 days"
</IfModule>
Lange Cache-Zeiten funktionieren nur mit versionierten Dateinamen (app.7f3a91.css). Ohne Hash im Namen bekommen wiederkehrende Besucher ein Jahr lang die alte Datei. Astro, Vite und die meisten Build-Werkzeuge setzen den Hash automatisch, klassische PHP-Projekte nicht.
Die Fallen, die erst live auffallen
[L] beendet nicht die Verarbeitung. In einer .htaccess heißt L nur: keine weitere Regel in diesem Durchlauf. Anschließend startet die Rewrite-Engine mit der neuen URL von vorn. Wer eine Endlosschleife vermeiden will, braucht eine Abbruchbedingung, meist RewriteCond %{REQUEST_FILENAME} !-f und !-d.
RewriteRule bricht am Leerzeichen ab. Das Muster wird an Leerzeichen in Felder getrennt. Enthält eine Alt-URL ein echtes Leerzeichen, muss es als \ maskiert oder als %20 geschrieben werden, sonst interpretiert Apache den Rest als Ziel und die Regel schlägt fehl.
RedirectMatch auf /index.html erzeugt eine Schleife, wenn DirectoryIndex dieselbe Datei ausliefert: Der Request auf / liefert intern /index.html, die Regel leitet auf /, und das Spiel beginnt von vorn. Lösung ist eine RewriteCond auf %{THE_REQUEST}, die nur den tatsächlich vom Browser gesendeten Pfad prüft.
Alte Regeln verschatten neue Seiten. Eine breite Regel wie RewriteRule ^produkte/ /shop/ [R=301,L] fängt auch produkte/neue-seite/ ab, die du gerade angelegt hast. Neue Ausnahmen gehören deshalb oberhalb der alten Regel, nicht darunter.
Messen statt annehmen
Jede Regel lässt sich vor dem Deploy prüfen. Lokal reicht ein Apache-Container mit derselben Verzeichnisstruktur; live prüfst du mit einem einzigen Kommando:
curl -sI https://example.com/alte-seite.htm | grep -iE '^(HTTP|location)'
Wichtig ist, gegen die echte Alt-URL-Liste zu testen und nicht nur gegen die Startseite. Ein Redirect, der die Root sauber weiterleitet und jede Unterseite ins Leere schickt, sieht bei einem Stichprobentest grün aus.
Häufige Fragen
Warum liefert meine Seite nach dem Speichern der .htaccess einen 500er?
In fast allen Fällen enthält die Datei eine Direktive, die der Hoster per AllowOverride nicht freigegeben hat, oder ein Modul fehlt. Kommentiere die letzte Änderung aus und arbeite dich zeilenweise vor. Die konkrete Ursache steht im error.log des Hosts, nicht im Browser.
Wirkt eine .htaccess auch in Unterverzeichnissen?
Ja. Apache wertet alle .htaccess-Dateien vom Dokumentwurzelverzeichnis bis zum angefragten Verzeichnis aus und legt sie übereinander. Eine Datei im Unterordner überschreibt nur die Direktiven, die sie selbst setzt.
Kostet die .htaccess Performance?
Auf Shared Hosting ist der Unterschied in der Praxis nicht messbar. Auf eigenen Servern lohnt sich der Umzug in den VirtualHost, weil Apache die Konfiguration dann einmal beim Start liest statt bei jedem Request erneut.
Funktioniert eine .htaccess unter nginx?
Nein. nginx kennt kein Äquivalent, Regeln gehören dort in die Serverkonfiguration und werden beim Reload übernommen. Wer häufig Regeln ändert, ohne Root-Zugriff zu haben, fährt mit Apache oder LiteSpeed besser.
Weiterlesen
- 301-Weiterleitungen richtig schreiben
Redirect, RedirectMatch und RewriteRule im Vergleich, Endlosschleifen vermeiden, Query-Strings erhalten, Domainwechsel sauber abbilden.
- Verzeichnisschutz mit mod_auth einrichten
Basic Auth per .htpasswd, Ausnahmen für einzelne Dateien, IP-Freigaben und warum Basic Auth ohne HTTPS wertlos ist.
- RegEx-Grundlagen für Apache-Regeln
Anker, Zeichenklassen, Gruppen und Backreferences - genau der Ausschnitt, den RewriteRule und RedirectMatch tatsächlich brauchen.
- Apache-Grundkonfiguration verstehen
VirtualHost, Module, MPM-Auswahl und Logformate: die Stellschrauben außerhalb der .htaccess.