.htaccess — Apache pro Verzeichnis konfigurieren

Praxis-Guide zu Apache .htaccess — Rewrites, Redirects, Zugriffsschutz, Caching, Kompression und Security-Header pro Verzeichnis konfigurieren.

Die .htaccess ist Apaches Werkzeug für Konfiguration pro Verzeichnis: Du legst eine solche Datei in einen Ordner, und ihre Direktiven gelten für genau dieses Verzeichnis und alle darunter – ohne Zugriff auf die zentrale Server-Konfiguration und ohne Neustart. Damit steuerst du Rewrites und Redirects (mod_rewrite), Zugriffsschutz, Basic Auth sowie Caching-, Kompressions- und Security-Header. Der Server wertet die Datei bei jedem Request neu aus, was Flexibilität gegen etwas Performance eintauscht – für statische Setups mit Vollzugriff ist die zentrale Konfiguration schneller. Damit Overrides überhaupt greifen, muss AllowOverride im VirtualHost sie erlauben. Dieser Guide zeigt dir die wichtigsten Direktiven für den Alltag, vom sauberen URL-Routing bis zum gehärteten Header-Set.

Grundlagen & Optionen

Zeilen, die mit # beginnen, sind Kommentare. Options -Indexes deaktiviert das Verzeichnis-Listing.

# Comment
Options -Indexes

Options +FollowSymLinks — Erlaubt Apache, symbolischen Links zu folgen. Für mod_rewrite erforderlich.

Options +FollowSymLinks

Options -Indexes +FollowSymLinks — Deaktiviert das Verzeichnis-Listing und aktiviert das Folgen von Symlinks (häufige Kombination).

Options -Indexes +FollowSymLinks

DirectoryIndex index.php index.html — Legt die Standarddateien fest, die Apache sucht, wenn ein Verzeichnis angefragt wird.

DirectoryIndex index.php index.html

DefaultType text/html — Setzt den Standard-MIME-Typ für Dateien ohne erkannte Endung.

DefaultType text/html

AddDefaultCharset UTF-8 — Hängt ;charset=UTF-8 an alle text/*-Content-Type-Header an.

AddDefaultCharset UTF-8

Weiterleitungen

Redirect 301 /old-page /new-page — Permanente Weiterleitung von /old-page nach /new-page. Browser und Suchmaschinen cachen sie.

Redirect 301 /old-page.html /new-page

Redirect 302 /temp /new-location — Temporäre Weiterleitung. Wird von Browsern und Suchmaschinen nicht gecacht.

Redirect 302 /temp /new-location

Redirect permanent /old https://example.com/new — Weiterleitung auf eine vollständige URL einer anderen Domain. 'permanent' entspricht 301.

Redirect permanent /blog https://blog.example.com/

RedirectMatch 301 ^/old-dir/(.*)$ /new-dir/$1 — Weiterleitung über ein Regex-Muster. Erfasst Pfadsegmente und verwendet sie erneut.

RedirectMatch 301 ^/posts/(.*)$ /blog/$1

RedirectMatch 301 \.html$ / — Leitet alle Anfragen, die auf .html enden, auf die Startseite um.

RedirectMatch 301 \.html$ /

mod_rewrite

RewriteEngine On — Aktiviert die Rewrite-Engine. Am Anfang jedes Rewrite-Blocks erforderlich.

RewriteEngine On

RewriteBase / — Setzt den Basis-URL-Pfad für Rewrite-Regeln. Für das Document-Root / verwenden.

RewriteBase /

RewriteRule ^old-page$ /new-page [R=301,L] — Leitet /old-page dauerhaft auf /new-page um. R=301 sendet eine Weiterleitung, L stoppt die Verarbeitung.

RewriteRule ^contact$ /contact-us [R=301,L]

RewriteRule ^(.*)$ /index.php [L,QSA] — Leitet alle Anfragen an index.php (Front-Controller-Muster für CMS-Frameworks). QSA hängt den Query-String an.

RewriteRule ^(.*)$ /index.php [L,QSA]

Leitet nur dann an index.php, wenn die Anfrage NICHT zu einer echten Datei oder einem Verzeichnis passt. Das Standardmuster von Drupal/WordPress.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?q=$1 [L,QSA]

HTTPS erzwingen: leitet alle HTTP-Anfragen auf ihr HTTPS-Äquivalent um.

RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

www erzwingen: leitet alle Anfragen ohne www auf www.example.com um.

RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

www entfernen: leitet www.example.com auf example.com um (nackte Domain).

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

RewriteRule ^(.+)/$ /$1 [R=301,L] — Entfernt abschließende Schrägstriche aus allen URLs (außer dem Root).

RewriteRule ^(.+)/$ /$1 [R=301,L]

Bildet eine saubere URL wie /page/3 intern auf /index.php?page=3 ab (ohne Weiterleitung).

RewriteCond %{QUERY_STRING} ^$
RewriteRule ^page/([0-9]+)/?$ /index.php?page=$1 [L,QSA]

RewriteRule-Flags

[L] — Last Rule. Stoppt die Verarbeitung weiterer Rewrite-Regeln, sobald diese passt.

RewriteRule ^ /index.php [L]

[R=301] — Redirect. Sendet eine HTTP-Weiterleitungs-Antwort (301 permanent, 302 temporär).

RewriteRule ^old$ /new [R=301,L]

[QSA] — Query String Append. Hängt den ursprünglichen Query-String an die neue URL an.

RewriteRule ^(.*)$ /index.php [L,QSA]

[NC] — No Case. Lässt das Muster ohne Beachtung der Groß-/Kleinschreibung passen.

RewriteRule ^contact$ /contact-us [NC,R=301,L]

[NE] — No Escape. URL-codiert Sonderzeichen in der Ersetzung nicht.

RewriteRule ^search$ /search?q=%{QUERY_STRING} [NE,L]

[P] — Proxy. Reicht die Anfrage über mod_proxy an einen Backend-Server weiter (interner Proxy).

RewriteRule ^api/(.*)$ http://localhost:3000/$1 [P,L]

[F] — Forbidden. Liefert für die passende Anfrage eine 403-Forbidden-Antwort.

RewriteRule \.env$ - [F,L]

[G] — Gone. Liefert eine 410-Gone-Antwort (die Ressource existiert nicht mehr).

RewriteRule ^deleted-page$ - [G,L]

Zugriffskontrolle

Require all granted — Erlaubt allen Besuchern den Zugriff (Apache 2.4+). Ersetzt Allow from all.

Require all granted

Require all denied — Verweigert allen Besuchern den Zugriff (Apache 2.4+). Ersetzt Deny from all.

Require all denied

Require ip 192.168.1.0/24 — Erlaubt den Zugriff nur aus einem bestimmten IP-Bereich.

Require ip 192.168.1.0/24

Require ip 203.0.113.5 — Erlaubt den Zugriff nur von einer bestimmten IP-Adresse.

Require ip 203.0.113.5

Erlaubt den Zugriff, wenn eine der aufgeführten Bedingungen erfüllt ist (ODER-Logik).

<RequireAny>
    Require ip 192.168.1.0/24
    Require ip 127.0.0.1
</RequireAny>

Sperrt den Zugriff auf eine bestimmte Datei (z. B. .env, wp-config.php, composer.json).

<Files ".env">
    Require all denied
</Files>

Sperrt den Zugriff auf Dateien, die zu einem Muster passen (z. B. Backups, Logs, Skripte).

<FilesMatch "\.(bak|sql|log|sh|ini)$">
    Require all denied
</FilesMatch>

Sperrt den direkten Zugriff auf sensible Dot-Dateien wie .htaccess und .htpasswd.

<FilesMatch "^\.(htaccess|htpasswd|env)">
    Require all denied
</FilesMatch>

Basic-Authentifizierung

Aktiviert HTTP-Basic-Authentifizierung. Nutzer brauchen einen Eintrag in der .htpasswd-Datei.

AuthType Basic
AuthName "Restricted Area"
AuthUserFile /path/to/.htpasswd
Require valid-user

htpasswd -c /path/to/.htpasswd <username> — Legt eine neue .htpasswd-Datei an und fügt den ersten Nutzer hinzu. Fragt das Passwort ab.

htpasswd -c /etc/apache2/.htpasswd admin

htpasswd /path/to/.htpasswd <username> — Fügt einer bestehenden .htpasswd-Datei einen neuen Nutzer hinzu (-c weglassen, um nicht zu überschreiben).

htpasswd /etc/apache2/.htpasswd editor

htpasswd -D /path/to/.htpasswd <username> — Entfernt einen Nutzer aus der .htpasswd-Datei.

htpasswd -D /etc/apache2/.htpasswd olduser

htpasswd -n <username> — Erzeugt einen gehashten Passwort-Eintrag und gibt ihn auf stdout aus (ohne in eine Datei zu schreiben).

htpasswd -n admin

Eigene Fehlerseiten

ErrorDocument 404 /404.html — Zeigt eine eigene HTML-Datei für 404-Fehler (Not Found).

ErrorDocument 404 /errors/404.html

ErrorDocument 403 /403.html — Zeigt eine eigene HTML-Datei für 403-Fehler (Forbidden).

ErrorDocument 403 /errors/403.html

ErrorDocument 500 /500.html — Zeigt eine eigene HTML-Datei für 500 Internal Server Error.

ErrorDocument 500 /errors/500.html

ErrorDocument 404 "Page not found" — Zeigt eine reine Textmeldung für 404-Fehler.

ErrorDocument 404 "Sorry, this page does not exist."

ErrorDocument 503 /maintenance.html — Zeigt eine Wartungsseite für 503 Service Unavailable.

ErrorDocument 503 /maintenance.html

Security-Header

Header always set X-Content-Type-Options "nosniff" — Verhindert, dass Browser den Content-Type per MIME-Sniffing abweichend vom deklarierten Typ erraten.

Header always set X-Content-Type-Options "nosniff"

Header always set X-Frame-Options "SAMEORIGIN" — Verhindert Clickjacking, indem die Seite nicht in iframes anderer Herkunft eingebettet werden darf.

Header always set X-Frame-Options "SAMEORIGIN"

Header always set X-XSS-Protection "1; mode=block" — Aktiviert den eingebauten XSS-Filter des Browsers und blockt die Seite, wenn ein Angriff erkannt wird.

Header always set X-XSS-Protection "1; mode=block"

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" — Aktiviert HTTP Strict Transport Security (HSTS) und erzwingt HTTPS für ein Jahr.

Header always set Strict-Transport-Security "max-age=31536000"

Header always set Referrer-Policy "strict-origin-when-cross-origin" — Steuert, wie viele Referrer-Informationen mit Anfragen gesendet werden.

Header always set Referrer-Policy "strict-origin-when-cross-origin"

Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()" — Deaktiviert Browser-Funktionen wie Geolocation, Mikrofon und Kamera.

Header always set Permissions-Policy "geolocation=()"

Header always set Content-Security-Policy "default-src 'self'" — Aktiviert eine Content Security Policy, um einzuschränken, welche Quellen Inhalte laden dürfen.

Header always set Content-Security-Policy "default-src 'self'; script-src 'self'"

Entfernt die Header X-Powered-By und Server, um Informationen zum Technologie-Stack zu verbergen.

Header unset X-Powered-By
Header unset Server

CORS-Header

Header always set Access-Control-Allow-Origin "*" — Erlaubt allen Ursprüngen Cross-Origin-Anfragen (öffentliche API).

Header always set Access-Control-Allow-Origin "*"

Header always set Access-Control-Allow-Origin "https://example.com" — Erlaubt Cross-Origin-Anfragen nur von einer bestimmten Domain.

Header always set Access-Control-Allow-Origin "https://app.example.com"

Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" — Legt fest, welche HTTP-Methoden für Cross-Origin-Anfragen erlaubt sind.

Header always set Access-Control-Allow-Methods "GET, POST, OPTIONS"

Header always set Access-Control-Allow-Headers "Content-Type, Authorization" — Legt fest, welche Request-Header bei Cross-Origin-Anfragen erlaubt sind.

Header always set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"

Header always set Access-Control-Max-Age "86400" — Cacht die Preflight-OPTIONS-Antwort für 24 Stunden, um Preflight-Anfragen zu reduzieren.

Header always set Access-Control-Max-Age "86400"

Kompression (mod_deflate)

Aktiviert gzip-Kompression für gängige textbasierte Content-Typen.

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json
</IfModule>

Deaktiviert die Kompression für Alt-Browser, die sie nicht korrekt verarbeiten.

BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|zip|gz|bz2|rar)$ no-gzip dont-vary — Überspringt die Kompression für bereits komprimierte Dateitypen (Bilder, Archive).

SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip

Caching (mod_expires)

Setzt mit mod_expires Browser-Cache-Ablaufzeiten je Dateityp.

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
    ExpiresByType text/html "access plus 1 hour"
</IfModule>

Header set Cache-Control "max-age=31536000, public" — Setzt den Cache-Control-Header für langlebige statische Assets (1 Jahr).

Header set Cache-Control "max-age=31536000, public, immutable"

Header set Cache-Control "no-cache, no-store, must-revalidate" — Deaktiviert das Caching für dynamische oder sensible Inhalte.

Header set Cache-Control "no-cache, no-store, must-revalidate"

FileETag MTime Size — Legt fest, welche Datei-Attribute zur Erzeugung von ETags für die Cache-Validierung verwendet werden.

FileETag MTime Size

PHP-Einstellungen

php_value upload_max_filesize 64M — Überschreibt die maximale Upload-Dateigröße für dieses Verzeichnis (nur mod_php).

php_value upload_max_filesize 64M

php_value post_max_size 64M — Überschreibt die maximale Größe eines POST-Requests (sollte >= upload_max_filesize sein).

php_value post_max_size 64M

php_value max_execution_time 300 — Überschreibt die maximale Skript-Laufzeit in Sekunden.

php_value max_execution_time 300

php_value memory_limit 256M — Überschreibt den maximalen Speicher, den ein Skript nutzen darf.

php_value memory_limit 256M

php_flag display_errors Off — Deaktiviert die PHP-Fehlerausgabe im Browser für die Produktion (für Entwicklung On verwenden).

php_flag display_errors Off

php_value session.cookie_httponly 1 — Markiert Session-Cookies als HttpOnly, um JavaScript-Zugriff zu verhindern.

php_value session.cookie_httponly 1

Fazit

Die .htaccess ist mächtig, weil sie Konfiguration genau dort erlaubt, wo der Code liegt – ideal für Shared Hosting oder pro-Projekt-Overrides. Weil Apache sie aber bei jedem Request neu liest, gilt: Was dauerhaft und global gelten soll, gehört in die zentrale Server-Konfiguration; nutze .htaccess gezielt für das, was wirklich pro Verzeichnis variiert. Wäge bei Weiterleitungen 301 (permanent, gecacht) und 302 (temporär) bewusst ab und schütze sensible Dateien wie .env oder .htpasswd explizit per Require all denied.

Verwandte Kommandos

  • apache – der Webserver, dessen Verhalten .htaccess pro Verzeichnis überschreibt
  • caddy – moderner Webserver mit automatischem HTTPS als Apache-Alternative
  • certbot – Let's-Encrypt-Zertifikate für HTTPS-Weiterleitungen ausstellen