Content Security Policy in Apache (.htaccess)
Eine Content Security Policy schränkt ein, welche Ressourcen der Browser auf Ihrer Apache-Website laden darf. Diese Anleitung zeigt das minimale, funktionierende Deployment: mod_headers aktivieren, Header in .htaccess eintragen, im Report-Only-Modus testen, dann auf Enforcing umschalten. Fünf Minuten Ende-zu-Ende, sobald mod_headers läuft.
Voraussetzung: mod_headers aktivieren
Apaches mod_headers-Modul ist das was der .htaccess erlaubt, Response-Header zu setzen. Auf Debian/Ubuntu-Derivaten:
sudo a2enmod headers
sudo systemctl restart apache2
Auf RHEL/CentOS/Fedora die /etc/httpd/conf/httpd.conf öffnen und sicherstellen, dass diese Zeile vorhanden und nicht auskommentiert ist:
LoadModule headers_module modules/mod_headers.so
Apache danach neu starten. Mit apachectl -M | grep headers prüfen Sie, ob das Modul wirklich geladen ist.
Das minimale .htaccess-Snippet
Öffnen Sie die .htaccess-Datei im Document-Root (oder legen Sie sie an, falls sie nicht existiert) und fügen Sie Folgendes ein:
<IfModule mod_headers.c>
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'"
</IfModule>
Ein paar Dinge zum Merken:
<IfModule mod_headers.c>schützt die Direktive davor, dass Apache bricht falls das Modul doch nicht geladen ist.Header always set— dasalways-Flag ist kritisch. Ohne es setzt Apache den Header nur auf 2xx-Responses. Heisst Ihre 404- und 500-Fehlerseiten haben gar keine CSP — und das sind genau die Seiten auf denen häufig ein XSS-Loch lauert, weil dort benutzerkontrollierter Content zurückgespiegelt wird.Content-Security-Policy-Report-Only— der Browser protokolliert Verstöße in der Konsole, blockiert aber nichts. Der sichere Weg zum Rollout.
Testen
Seite neu laden, DevTools (F12) → Konsole. Sie sehen rote Warnungen für jede Ressource die blockiert werden würde, z. B.:
[Report Only] Refused to load the script 'https://cdn.example.com/analytics.js' because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
Jede dieser Zeilen ist eine echte Ressource die Ihre Seite lädt und die die aktuelle Policy nicht abdeckt. Sie haben zwei Optionen: Domain in die passende Direktive aufnehmen, oder die Abhängigkeit loswerden. Iterieren bis die Konsole sauber ist.
.htaccess einfügen.
In den Enforcing-Modus wechseln
Sobald die Konsole ruhig bleibt, ändern Sie den Header-Namen von Content-Security-Policy-Report-Only zu Content-Security-Policy und laden neu:
<IfModule mod_headers.c>
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'"
</IfModule>
Fertig. Ihre Seite setzt jetzt eine CSP durch. Ab diesem Moment wird jeder Verstoß vom Browser wirklich blockiert, nicht nur gemeldet.
Typische Fehlerquellen
„mod_headers is not loaded"
Wenn das im Apache-Error-Log auftaucht, haben Sie die Voraussetzung übersprungen. sudo a2enmod headers && sudo systemctl restart apache2 (Debian/Ubuntu) oder die LoadModule-Zeile in der httpd.conf auskommentieren.
Der Header ist da, aber die Policy wird ignoriert
Prüfen Sie, ob an einer anderen Stelle ein zweiter CSP-Header gesetzt wird (von PHP, einer VirtualHost-Datei, einer anderen .htaccess). Wenn mehrere Content-Security-Policy-Header gesendet werden, nehmen Browser die Schnittmenge — und die ist oft restriktiver als beabsichtigt. Mit curl -I https://ihre-seite.example sehen Sie alle Header. Duplikate eliminieren und nur den einen aus .htaccess behalten.
WordPress/PHP-Anwendungen brauchen 'unsafe-inline'
Die meisten CMS und PHP-Frameworks erzeugen Inline-<script>- und <style>-Blöcke. Ohne 'unsafe-inline' in script-src und style-src bricht die Seite. Die striktere Alternative wären Nonces oder Hashes, aber das erfordert Code-Anpassungen — Details in der WordPress-Anleitung.
CORS und CSP sind zwei verschiedene Dinge
Ein häufiges Missverständnis: Wenn Ihre Seite beim Laden einer Ressource blockiert wird, kann das CORS (Access-Control-Allow-Origin) sein und nicht CSP. CSP regelt was Ihre Seite laden darf; CORS regelt, ob der andere Server bereit ist, von Ihrer Seite aus geladen zu werden. Beide können Ressourcen blockieren, die Lösung ist jeweils eine andere.
Dev-Server mit Hot-Reload
Webpack Dev Server, Vite, Next.js im Dev-Modus — die injizieren alle Inline-Skripte für Hot Reloading. Wenn Ihre CSP das blockiert, bricht die Dev-Umgebung zusammen. Lösung: die strikte CSP nur in Produktion ausrollen. In Apache lässt sich das über SetEnvIf lösen, oder einfach indem die .htaccess mit der CSP nur in Produktion deployt wird.
Apache-spezifische Feinheiten
.htaccess und Performance
Jede Direktive in der .htaccess wird bei jedem Request neu ausgewertet. Für ein einzelnes Header set ist der Overhead vernachlässigbar, aber wenn Sie maximale Performance wollen, verschieben Sie die Direktive in den VirtualHost-Block unter /etc/apache2/sites-enabled/ihre-seite.conf und deaktivieren .htaccess komplett mit AllowOverride None. Gleiche Syntax, kein Overhead pro Request.
Header append vs. Header set
Nehmen Sie immer set, niemals append. append würde mehrere CSP-Werte aneinanderhängen, und Browser behandeln das als Schnittmenge — in der Regel nicht das was Sie wollen.
Reporting-Endpoints
Sobald Sie im Enforcing-Modus sind, lohnt sich eine report-uri-Direktive, damit Sie Verstöße sehen die draußen in freier Wildbahn passieren (nicht nur bei Ihren eigenen Tests):
Header always set Content-Security-Policy "default-src 'self'; ... ; report-uri https://ihr-reporting-endpunkt.example/csp"
Dienste wie report-uri.com bieten kostenlose Stufen; Sie können aber auch selber einen Collector hosten.
Ihre exakte CSP in 30 Sekunden generieren → Wir crawlen Ihre Seite, finden jede Ressource, und spucken einen .htaccess-Block aus der zu Ihrem echten Stack passt — kein Rumraten