Welche Header sinnvoll sind
- Strict-Transport-Security (HSTS): Browser rufen die Seite nur noch per HTTPS auf.
- Content-Security-Policy (CSP): legt fest, woher Skripte, Styles und Frames kommen dürfen. Der wirksamste Schutz gegen XSS, aber auch der aufwendigste.
- X-Content-Type-Options: nosniff: verhindert, dass Browser Dateitypen erraten.
- Referrer-Policy: begrenzt, was beim Klick auf fremde Links übertragen wird.
- Permissions-Policy: schaltet Kamera, Mikrofon, Standort usw. ab, wenn du sie nicht brauchst.
- frame-ancestors (in der CSP) oder X-Frame-Options: Schutz vor Clickjacking.
Nicht mehr nötig ist X-XSS-Protection. Moderne Browser ignorieren ihn, in alten konnte er sogar schaden.
nginx
# /etc/nginx/snippets/security-headers.conf add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; add_header Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" always; # im server-Block: include snippets/security-headers.conf; server_tokens off;
Zwei Fallen gibt es bei nginx:
- Ohne
alwaysfehlen die Header bei Fehlerseiten und Weiterleitungen (z. B. 404, 301). - Keine Vererbung: Sobald ein
location-Block ein eigenesadd_headerhat, gelten die Header aus demserver-Block dort nicht mehr. Deshalb die Datei perincludeauch in dieselocation-Blöcke einbinden.
Apache
# sudo a2enmod headers
<VirtualHost *:443>
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header always set Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
Header always unset X-Powered-By
</VirtualHost>
# global, z. B. in security.conf
ServerTokens Prod
ServerSignature Off
Auf Webhosting ohne Zugriff auf die Serverkonfiguration funktionieren dieselben Header-Zeilen meist auch in der .htaccess im Hauptverzeichnis. Die PHP-Version im Header X-Powered-By schaltest du mit expose_php = Off in der php.ini ab.
Die CSP einführen, ohne die Seite kaputt zu machen
Eine zu strenge CSP blockiert eigene Skripte, eingebettete Videos oder Analyse-Tools. Deshalb in Stufen vorgehen:
- Zuerst als
Content-Security-Policy-Report-Onlysetzen. Der Browser blockiert nichts, meldet Verstöße aber in der Konsole. - Die Konsole auf den wichtigsten Seiten durchgehen und benötigte Quellen ergänzen, z. B.
script-src 'self' https://cdn.example.net. - Inline-Skripte in Dateien auslagern oder mit einer Nonce versehen.
'unsafe-inline'beiscript-srchebelt den XSS-Schutz weitgehend aus. - Erst dann auf
Content-Security-Policyumstellen.
Vorsicht bei HSTS
HSTS lässt sich nicht einfach zurücknehmen: Browser merken sich die Vorgabe für die angegebene Zeit. Fang mit max-age=300 an, prüfe, ob wirklich alle Subdomains HTTPS können, und erhöhe dann auf zwei Jahre. includeSubDomains betrifft auch interne Namen wie intranet.example.com, wenn sie unter derselben Domain liegen.
Prüfen
curl -sI https://example.com curl -sI https://example.com/gibt-es-nicht # auch bei 404?
Der Security-Header-Check bewertet alle Header, erkennt typische Fehler wie 'unsafe-inline' und erzeugt die passenden Zeilen für deinen Server.
FAQ
Reicht X-Frame-Options oder brauche ich frame-ancestors?
frame-ancestors in der CSP ist der moderne Weg und erlaubt auch mehrere Quellen. Für sehr alte Browser schadet X-Frame-Options: SAMEORIGIN zusätzlich nicht.
Warum sehe ich die Header doppelt?
Meist setzen sie sowohl die Anwendung (z. B. PHP oder ein CMS) als auch der Webserver. Doppelte CSP-Header werden beide angewendet, die strengere gewinnt. Am besten nur an einer Stelle setzen.
Gehören Security-Header auch auf APIs?
Ja, zumindest HSTS, nosniff und eine knappe CSP wie default-src 'none'; frame-ancestors 'none'. Sie kosten nichts und verhindern, dass Antworten im Browser missbraucht werden.
Setzt ein CDN die Header für mich?
Nur wenn du es dort einrichtest. Viele CDNs reichen die Header des Ursprungs-Servers einfach durch.