# .htaccess — Apache pro Verzeichnis konfigurieren

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

Source: https://www.jpkc.com/db/cheatsheets/web-servers/htaccess/

<!-- PROSE:intro -->
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.
<!-- PROSE:intro:end -->

## Grundlagen & Optionen

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

```bash
# Comment
Options -Indexes
```

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

```bash
Options +FollowSymLinks
```

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

```bash
Options -Indexes +FollowSymLinks
```

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

```bash
DirectoryIndex index.php index.html
```

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

```bash
DefaultType text/html
```

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

```bash
AddDefaultCharset UTF-8
```

## Weiterleitungen

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

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

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

```bash
Redirect 302 /temp /new-location
```

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

```bash
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.

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

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

```bash
RedirectMatch 301 \.html$ /
```

## mod_rewrite

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

```bash
RewriteEngine On
```

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

```bash
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.

```bash
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.

```bash
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.

```bash
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.

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

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

```bash
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).

```bash
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).

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

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

```bash
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.

```bash
RewriteRule ^ /index.php [L]
```

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

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

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

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

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

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

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

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

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

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

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

```bash
RewriteRule \.env$ - [F,L]
```

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

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

## Zugriffskontrolle

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

```bash
Require all granted
```

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

```bash
Require all denied
```

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

```bash
Require ip 192.168.1.0/24
```

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

```bash
Require ip 203.0.113.5
```

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

```bash
<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).

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

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

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

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

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

## Basic-Authentifizierung

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

```bash
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.

```bash
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).

```bash
htpasswd /etc/apache2/.htpasswd editor
```

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

```bash
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).

```bash
htpasswd -n admin
```

## Eigene Fehlerseiten

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

```bash
ErrorDocument 404 /errors/404.html
```

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

```bash
ErrorDocument 403 /errors/403.html
```

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

```bash
ErrorDocument 500 /errors/500.html
```

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

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

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

```bash
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.

```bash
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.

```bash
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.

```bash
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.

```bash
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.

```bash
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.

```bash
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.

```bash
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.

```bash
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).

```bash
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.

```bash
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.

```bash
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.

```bash
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.

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

## Kompression (mod_deflate)

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

```bash
<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.

```bash
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).

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

## Caching (mod_expires)

Setzt mit mod_expires Browser-Cache-Ablaufzeiten je Dateityp.

```bash
<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).

```bash
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.

```bash
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.

```bash
FileETag MTime Size
```

## PHP-Einstellungen

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

```bash
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).

```bash
php_value post_max_size 64M
```

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

```bash
php_value max_execution_time 300
```

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

```bash
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).

```bash
php_flag display_errors Off
```

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

```bash
php_value session.cookie_httponly 1
```

<!-- PROSE:outro -->
## 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`.

## Weiterführende Links

- [mod_rewrite – offizielle Apache-Dokumentation](https://httpd.apache.org/docs/current/mod/mod_rewrite.html) – Referenz zu RewriteRule und RewriteCond (englisch)
- [.htaccess-Tutorial – Apache HOWTO](https://httpd.apache.org/docs/current/howto/htaccess.html) – Einführung und Grundlagen (englisch)
- [.htaccess – Wikipedia](https://de.wikipedia.org/wiki/.htaccess) – Hintergrund und Funktionsweise
<!-- PROSE:outro:end -->

## Verwandte Kommandos

- [apache](https://www.jpkc.com/db/cheatsheets/web-servers/apache/) – der Webserver, dessen Verhalten .htaccess pro Verzeichnis überschreibt
- [caddy](https://www.jpkc.com/db/cheatsheets/web-servers/caddy/) – moderner Webserver mit automatischem HTTPS als Apache-Alternative
- [certbot](https://www.jpkc.com/db/cheatsheets/web-servers/certbot/) – Let's-Encrypt-Zertifikate für HTTPS-Weiterleitungen ausstellen

