Webserver mit Security-Headern absichern – sinnvoll statt blind kopieren

Überarbeitet am 8. September 2026.

Security-Header sind eine schöne zusätzliche Schutzschicht für Webseiten. Mein alter Artikel hatte allerdings genau das Problem, vor dem ich heute warnen würde: Ich hatte mehrere Header global auf sämtliche Websites gesetzt, ohne die Folgen für jede Anwendung einzeln zu prüfen.

Das ging sogar einmal schief. Eine globale Regel, die jedes Set-Cookie nachträglich mit HttpOnly; Secure versehen sollte, sorgte bei mir dafür, dass ein Cookie-Banner nicht mehr richtig funktionierte. Seitdem gilt für mich: Security-Header nicht nach Punkteliste einschalten, sondern verstehen, testen und passend zur Anwendung setzen.

1. X-Content-Type-Options: nosniff

Dieser Header ist ein guter und meist unkomplizierter Anfang:

X-Content-Type-Options: nosniff

Er weist den Browser an, die vom Server angegebenen MIME-Typen zu respektieren, statt den Inhalt selbst anders zu interpretieren.

Nginx:

add_header X-Content-Type-Options "nosniff" always;

Apache:

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

2. Referrer-Policy

Mit der Referrer-Policy lässt sich begrenzen, welche Informationen eine aufgerufene Fremdseite über die Herkunft eines Besuchers erhält.

Ein vernünftiger allgemeiner Wert ist:

Referrer-Policy: strict-origin-when-cross-origin

Das ist inzwischen auch der Browser-Standard. Wer noch weniger Informationen nach außen geben möchte, kann beispielsweise strict-origin, same-origin oder no-referrer prüfen. Die strengste Einstellung ist aber nicht automatisch die beste, wenn Anwendungen Referrer-Informationen benötigen.

3. HSTS – sehr sinnvoll, aber nicht leichtfertig mit preload

HTTP Strict Transport Security teilt dem Browser mit, dass eine Domain künftig nur über HTTPS angesprochen werden soll:

Strict-Transport-Security: max-age=31536000

Nginx:

add_header Strict-Transport-Security "max-age=31536000" always;

Apache:

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

Wichtig: Den Header nur über HTTPS ausliefern. Browser ignorieren HSTS über eine unverschlüsselte HTTP-Verbindung ohnehin.

Mit includeSubDomains gilt die Regel zusätzlich für sämtliche Subdomains. Das sollte man erst aktivieren, wenn wirklich jede aktuelle und zukünftige Subdomain per HTTPS funktioniert.

Und preload würde ich nicht mehr wie früher einfach in eine Beispielkonfiguration schreiben. Wer seine Domain in eine Browser-Preload-Liste eintragen lässt, bindet sich sehr stark an HTTPS. Vorher unbedingt alle Subdomains prüfen und die Folgen verstehen.

4. Schutz vor Einbettung: CSP frame-ancestors

Früher wurde dafür häufig nur X-Frame-Options gesetzt. Dieser Header funktioniert weiterhin, moderner und flexibler ist aber die CSP-Direktive frame-ancestors.

Wenn eine Seite überhaupt nicht in Frames eingebettet werden soll:

Content-Security-Policy: frame-ancestors 'none';

Wenn Einbettung aus derselben Origin erlaubt sein soll:

Content-Security-Policy: frame-ancestors 'self';

Bei Nextcloud, Office-Anwendungen, Payment-Diensten oder eingebetteten Inhalten sollte man so eine Regel aber nicht blind setzen. Genau dort können Frames legitim benötigt werden.

5. Content-Security-Policy: mächtig, aber individuell

CSP kann unter anderem verhindern, dass unerwartete Skripte, Styles oder andere Ressourcen geladen werden. Sie gehört aber nicht in eine universelle Copy-and-Paste-Zeile für WordPress, Nextcloud und jede andere Anwendung.

Mein alter Artikel hatte beispielsweise eine lange CSP mit 'unsafe-inline' und 'unsafe-eval' sowie fest verdrahteten Fremddomains. So etwas altert schnell und kann entweder Funktionen blockieren oder ein falsches Gefühl von Sicherheit erzeugen.

Mein heutiger Weg wäre:

  1. prüfen, welche Ressourcen die Anwendung tatsächlich benötigt,
  2. eine CSP zunächst mit Content-Security-Policy-Report-Only testen,
  3. Verstöße auswerten,
  4. danach schrittweise eine wirksame CSP aktivieren.

Bei fertigen Anwendungen wie Nextcloud würde ich außerdem zuerst deren offizielle Dokumentation beziehungsweise bereits gesetzte Header prüfen, statt darüber global eine eigene Richtlinie zu legen.

6. X-XSS-Protection nicht mehr als modernen Schutz empfehlen

In meinem alten Beispiel stand:

X-XSS-Protection: 1; mode=block

Diesen Header würde ich heute nicht mehr als moderne Sicherheitsmaßnahme aufnehmen. Die entsprechenden alten Browser-XSS-Filter sind veraltet. Moderne Anwendungen sollten XSS vor allem durch korrektes Escaping, sichere Frameworks und eine sinnvoll gestaltete Content Security Policy verhindern.

7. CORS nicht global mit * freigeben

Ebenfalls problematisch war mein altes Beispiel:

Access-Control-Allow-Origin: *

CORS ist keine allgemeine „Webseite sicherer machen“-Option. Damit wird festgelegt, welche fremden Origins Browser auf bestimmte Ressourcen zugreifen lassen. Ein Wildcard-Wert kann für öffentliche APIs oder statische öffentliche Ressourcen sinnvoll sein, gehört aber nicht pauschal auf jede Website.

Wenn eine Anwendung CORS benötigt, sollte die erlaubte Origin gezielt passend zu genau diesem Anwendungsfall gesetzt werden.

8. Cookies nicht global im Webserver umschreiben

Genau damit hatte ich mir damals selbst Probleme gebaut:

Header always edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure

Die Absicht war gut: Cookies sollten nur über HTTPS übertragen und vor JavaScript geschützt werden. Aber nicht jedes Cookie darf HttpOnly sein. Ein Cookie, das clientseitiges JavaScript bewusst lesen muss, funktioniert danach möglicherweise nicht mehr.

Cookie-Eigenschaften wie Secure, HttpOnly und SameSite sollten deshalb vorzugsweise von der Anwendung beziehungsweise dem Framework passend zum jeweiligen Cookie gesetzt werden – nicht per globalem Such-und-Ersetzen im Webserver.

9. Permissions-Policy nur gezielt verwenden

Mit Permissions-Policy können Browserfunktionen wie Kamera, Mikrofon oder Geolocation eingeschränkt werden. Die Browserunterstützung ist allerdings je nach Direktive unterschiedlich. Auch hier gilt: Nicht wahllos alles abschalten, wenn eine Anwendung beispielsweise Videotelefonie oder Standortzugriff benötigt.

Für eine einfache Webseite ohne solche Funktionen könnte man einzelne Features bewusst sperren, beispielsweise:

Permissions-Policy: geolocation=(), camera=(), microphone=()

Vor dem Produktiveinsatz immer die tatsächlich benötigten Funktionen und Browserkompatibilität prüfen.

Meine kleine Basis für normale Webseiten

Für eine klassische HTTPS-Webseite ohne besondere Anforderungen könnte eine vorsichtige Nginx-Basis beispielsweise so beginnen:

add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000" always;

Bei Apache entsprechend:

Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Strict-Transport-Security "max-age=31536000"

CSP, frame-ancestors, CORS und Permissions Policy würde ich anschließend pro Anwendung ergänzen und testen.

Header überprüfen

Nach Änderungen zunächst Webserver-Konfiguration testen:

sudo nginx -t

beziehungsweise:

sudo apachectl configtest

Die tatsächlichen Response-Header lassen sich beispielsweise mit curl ansehen:

curl -I https://example.de

Zusätzlich sind Browser-Entwicklertools und der Mozilla Observatory hilfreich.

Fazit

Security-Header sind sinnvoll – aber mehr Header bedeutet nicht automatisch mehr Sicherheit. Mein größter Lernpunkt aus der alten Konfiguration ist, globale Regeln nur dann zu setzen, wenn ich sicher bin, dass sie zu allen darunter laufenden Anwendungen passen.

Für Nextcloud habe ich die wichtigsten Servermaßnahmen separat beschrieben: Nextcloud richtig absichern. Und falls Nginx selbst einmal nicht startet: Nginx „Unit is masked“ beheben.

Quellen: MDN: HSTS, MDN: X-Content-Type-Options, MDN: Referrer-Policy und MDN: CSP frame-ancestors.

Wie ist deine Meinung?
Schreib gern einen Kommentar unter dem Artikel oder diskutiere mit anderen im Matrix-Kanal: #dasnetzundich:matrix.org

2 Kommentare zu „Webserver mit Security-Headern absichern – sinnvoll statt blind kopieren“

Schreibe einen Kommentar