StartseiteRatgeber › Security-Header erklärt: HSTS, CSP und die anderen

Security-Header erklärt: HSTS, CSP und die anderen

Welche Schutz-Kopfzeilen fehlen dir? Adresse eintragen, wir lesen aus, was dein Server mitschickt.

Geprüft wird die ganze Seite, nicht nur dieser Punkt. Wir rufen sie ab wie jeder Besucher — es wird nichts verändert.

Wie verbreitet fehlende Kopfzeilen tatsächlich sind, haben wir an 352 deutschen Websites gemessen: 75,3 % ohne Content-Security-Policy, 51,1 % ohne HSTS, 27,0 % ohne eine einzige der vier. Zur Studie 2026.

Security-Header sind kurze Anweisungen, die dein Server bei jeder Antwort mitschickt. Sie kosten nichts, verlangsamen nichts und verhindern trotzdem ganze Angriffsklassen. Der Aufwand liegt meist bei wenigen Zeilen in der Serverkonfiguration — mit zwei Ausnahmen, bei denen man vorsichtig sein muss. Dieser Text nennt für jeden Header den konkreten Wert, die Konfiguration für Apache, nginx und Plesk, und die Fallen, an denen es in der Praxis scheitert.

Betrifft dich das? Prüf deine Website kostenlos — dauert 30 Sekunden, keine Anmeldung nötig.

Die Reihenfolge, in der man sie einführt

Nicht alle Header sind gleich riskant. Wer bei null anfängt, geht am besten so vor:

SchrittHeaderRisiko
1X-Content-Type-Options, Referrer-Policy, X-Frame-Optionspraktisch null — sofort setzen
2Permissions-Policygering, nur prüfen ob Karten oder Videokonferenzen eingebunden sind
3Strict-Transport-Securitymittel — vorher HTTPS auf allen Unterdomains prüfen
4Content-Security-Policyhoch — erst im Report-Only-Modus, das braucht Zeit

Schritt 1 und 2 sind in zehn Minuten erledigt und decken bereits mehrere Angriffsklassen ab. Wer Schritt 4 aufschiebt, hat trotzdem schon viel gewonnen.

Die vier Kern-Header

HeaderVerhindertGuter Wert
Strict-Transport-SecurityHerabstufung auf unverschlüsseltes HTTPmax-age=31536000; includeSubDomains
Content-Security-PolicyEingeschleuste Skripte (XSS)siehe unten — abhängig von der Seite
X-Frame-OptionsEinbetten in fremde Seiten (Clickjacking)SAMEORIGIN
X-Content-Type-OptionsFalsch geratene Dateitypennosniff

Strict-Transport-Security (HSTS)

Tippt jemand deine-seite.de in die Adresszeile, versucht der Browser es zunächst unverschlüsselt. Diese erste Anfrage lässt sich in einem fremden WLAN abfangen und umleiten — die Weiterleitung auf HTTPS kommt zu spät. HSTS löst das: Der Browser merkt sich, dass diese Domain nur über HTTPS erreichbar ist, und ruft sie ab dem zweiten Besuch gar nicht erst unverschlüsselt auf.

Entscheidend ist der Wert, nicht die bloße Anwesenheit des Headers. max-age=300 ist faktisch wirkungslos — fünf Minuten Schutz. Empfohlen ist ein Jahr:

Strict-Transport-Security: max-age=31536000; includeSubDomains

Vorsicht: Mit includeSubDomains gilt die Vorgabe auch für alle Unterdomains. Läuft dort etwas noch ohne HTTPS — ein alter Entwicklungsserver etwa —, ist es für die Dauer von max-age nicht mehr erreichbar. Erst prüfen, dann setzen.

preload nur ergänzen, wenn du dir wirklich sicher bist: Die Eintragung in die Browser-Vorlagelisten lässt sich nur mit Verzögerung rückgängig machen.

Der sichere Einstieg

Wer unsicher ist, fängt klein an und erhöht:

  1. max-age=300 ohne includeSubDomains — eine Woche laufen lassen und beobachten, ob sich jemand meldet.
  2. max-age=86400 (ein Tag), dann alle Unterdomains einzeln im Browser aufrufen und prüfen, dass jede per HTTPS erreichbar ist.
  3. Erst dann max-age=31536000; includeSubDomains.

Der Grund für die Vorsicht: Ein zu früh gesetztes includeSubDomains lässt sich nicht zurückrufen. Der Wert steckt im Browser des Besuchers, nicht auf deinem Server. Man kann ihn nur auf 0 setzen und warten, bis alle Besucher die Seite noch einmal aufrufen.

Content-Security-Policy (CSP)

Die CSP legt fest, aus welchen Quellen der Browser überhaupt etwas laden darf. Schafft es Angreifercode in deine Seite — etwa über ein Kommentarfeld —, verhindert eine gute CSP, dass er ausgeführt wird.

Der häufigste Fehler ist eine Policy, die zwar vorhanden, aber wirkungslos ist:

Content-Security-Policy: script-src 'self' 'unsafe-inline'

'unsafe-inline' erlaubt beliebigen Skriptcode direkt im HTML — also genau das, wovor die CSP schützen soll. In Prüfberichten sieht so eine Seite gut aus, geschützt ist sie nicht.

Die wichtigsten Direktiven

DirektiveRegeltEmpfehlung
default-srcAuffangregel für alles, was nicht eigens genannt ist'self'
script-srcJavaScript'self', bei Bedarf mit Nonce
style-srcStylesheets'self'
img-srcBilder'self' data:
connect-srcfetch, XHR, WebSockets'self'
object-srcFlash und andere Plugins'none' — es gibt keinen legitimen Grund mehr
base-uridas <base>-Tag'self' — sonst lassen sich alle relativen Pfade umbiegen
frame-ancestorswer die Seite einbetten darf'self'
form-actionwohin Formulare abgeschickt werden dürfen'self'
upgrade-insecure-requestshebt http-Ressourcen automatisch auf httpssetzen, kostet nichts

object-src 'none' und base-uri 'self' werden fast immer vergessen, obwohl beide keinerlei Nebenwirkungen haben. Sie gehören in jede Policy.

Ein brauchbarer Ausgangspunkt für eine einfache Seite:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self';
  img-src 'self' data:; object-src 'none'; frame-ancestors 'self'; base-uri 'self';
  form-action 'self'; upgrade-insecure-requests

Inline-Skripte: der Nonce

Wer Inline-Skripte braucht, arbeitet mit einem Nonce: Der Server erzeugt pro Aufruf einen Zufallswert, schreibt ihn in den Header und in das erlaubte Skript-Tag. Fremder Code kennt den Wert nicht.

Content-Security-Policy: script-src 'self' 'nonce-r4nd0mW3rt'

<script nonce="r4nd0mW3rt"> ... </script>

Zwei Regeln dazu: Der Wert muss bei jedem Seitenaufruf neu erzeugt werden — ein fest eingetragener Nonce ist wertlos. Und er darf nicht vorhersagbar sein, also aus einem kryptografischen Zufallsgenerator stammen, nicht aus einem Zeitstempel.

Oft ist der einfachere Weg, die Inline-Skripte in eine eigene Datei auszulagern. Dann reicht script-src 'self' ohne jeden Sonderfall. Achtung dabei: Ausgelagerter Code läuft später als Inline-Code — wenn das Skript auf Elemente zugreift, die es vorher direkt im Fluss gab, muss man die Ausführungsreihenfolge prüfen.

Kein Verstoß gegen die CSP sind übrigens <script type="application/ld+json">-Blöcke mit strukturierten Daten. Sie enthalten keinen ausführbaren Code und werden von der Direktive nicht erfasst — sie dürfen also stehen bleiben.

Die Einführung in drei Schritten

  1. Report-Only. Erst mit Content-Security-Policy-Report-Only einsetzen. Der Browser meldet Verstöße in der Entwicklerkonsole, blockiert aber nichts.
  2. Beobachten. Ein paar Tage laufen lassen und alle Seitentypen durchklicken — Formulare, Shop, Buchung, Kartenansicht. Was in der Konsole auftaucht, entweder erlauben oder loswerden.
  3. Umschalten. Wenn nichts Relevantes mehr auftaucht, auf den echten Header umstellen. Danach noch einmal alle Seitentypen prüfen.

Wer Verstöße nicht von Hand in der Konsole sammeln will, kann sie sich melden lassen:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-bericht

Dann schickt der Browser jeden Verstoß als JSON an diese Adresse. Das lohnt sich vor allem bei Seiten mit vielen Unterseiten, wo man nicht alles selbst durchklicken kann.

Die kleineren Header

Wenn Anmeldung über ein Popup läuft — etwa bei PayPal oder einem Single-Sign-on — darf Cross-Origin-Opener-Policy nicht auf same-origin stehen, sonst bricht die Rückmeldung aus dem Popup ab. Richtig ist dann same-origin-allow-popups. Das ist ein Fehler, der erst beim Bezahlen auffällt.

Was heute weg kann

X-XSS-Protection steht noch auf vielen Servern. Der Header schaltete einen Browser-Filter ein, den es in modernen Browsern gar nicht mehr gibt — und der in alten Versionen selbst Sicherheitslücken aufriss. Er kann ersatzlos entfernt werden; seine Aufgabe übernimmt die CSP. Dasselbe gilt für Expect-CT, das seit 2021 wirkungslos ist.

Einrichten

Apache

<IfModule mod_headers.c>
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "geolocation=(), camera=(), microphone=()"
</IfModule>

Das always ist wichtig: Ohne dieses Schlüsselwort fehlen die Header bei Fehlerseiten. Gerade die 404-Seite ist aber die, die Angreifer am häufigsten zu sehen bekommen.

nginx

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

nginx-Falle: Sobald in einem location-Block ein einziges add_header steht, gelten die Header aus dem übergeordneten Block dort nicht mehr. Sie müssen dann im Unterblock wiederholt werden. Das ist einer der häufigsten Gründe dafür, dass Header auf der Startseite erscheinen, auf Unterseiten aber fehlen.

Plesk

In Plesk führt der Weg über Websites & Domains → Apache- & nginx-Einstellungen. Dort gibt es zwei Felder: eines für zusätzliche Apache-Direktiven, eines für nginx. Läuft nginx als Reverse-Proxy vor Apache — die Standardkonfiguration —, gehören die Header in das nginx-Feld, sonst werden sie unter Umständen überschrieben. Nach dem Speichern lädt Plesk die Konfiguration selbst neu.

WordPress ohne Serverzugriff

Wenn du nicht an die Serverkonfiguration kommst, lassen sich die Header in der .htaccess im Wurzelverzeichnis setzen — mit demselben Block wie oben. Vorher eine Sicherungskopie anlegen: Ein Tippfehler in der .htaccess legt die ganze Seite lahm. Setze den Block über den WordPress-Abschnitt, sonst wird er beim nächsten Speichern der Permalink-Einstellungen überschrieben.

Wenn ein CDN davor hängt

Cloudflare und ähnliche Dienste können Header hinzufügen, ersetzen oder verschlucken. Zwei Konsequenzen: Erstens misst du mit einem Prüfdienst unter Umständen die Header des CDN, nicht deine eigenen. Zweitens kann ein im CDN gesetzter Header einen falsch konfigurierten Server verdecken — fällt das CDN aus oder wird umgangen, steht die Seite plötzlich ohne da. Setze die Header deshalb am Ursprungsserver und benutze das CDN höchstens ergänzend.

Danach prüfen

Header werden erstaunlich oft gesetzt und wirken trotzdem nicht — wegen Zwischenspeichern, wegen eines vorgeschalteten Proxys oder wegen der nginx-Falle. Am schnellsten geht die Kontrolle auf der Kommandozeile:

curl -sI https://deine-seite.de | grep -i -E "strict-transport|content-security|x-frame|x-content|referrer|permissions"

Das Wichtige daran: mehrere Unterseiten prüfen, nicht nur die Startseite — und auch eine Adresse, die es nicht gibt, damit die Fehlerseite mit dabei ist.

Apexly bewertet dabei nicht nur, ob ein Header da ist, sondern auch, ob sein Wert tatsächlich schützt — ein max-age=300 oder eine CSP mit 'unsafe-inline' zählt bei uns nicht als erfüllt.

Die häufigsten Fehler

FehlerFolge
CSP mit 'unsafe-inline'Header vorhanden, Schutz nicht
HSTS mit sehr kurzem max-agefaktisch wirkungslos
always in Apache vergessenFehlerseiten ohne Header
nginx: add_header im UnterblockHeader verschwinden auf Unterseiten
COOP auf same-origin trotz Bezahl-PopupZahlung bricht ab
X-XSS-Protection noch gesetztwirkungslos, in alten Browsern sogar schädlich
Nur die Startseite geprüftfalsches Sicherheitsgefühl

Häufige Fragen

Welcher Security-Header ist am wichtigsten?

Wenn du nur einen setzen kannst: Strict-Transport-Security. Er ist in einer Zeile erledigt, kann kaum etwas kaputtmachen und verhindert eine ganze Angriffsklasse. Die Content-Security-Policy bringt am meisten, ist aber auch am aufwendigsten.

Kann ein Security-Header meine Website kaputtmachen?

Eine zu strenge Content-Security-Policy kann Skripte blockieren und damit sichtbar Funktionen lahmlegen. Deshalb erst im Report-Only-Modus testen. HSTS ist gefährlich, wenn HTTPS nicht überall funktioniert — Browser merken sich die Vorgabe für die angegebene Dauer. Die übrigen Header sind praktisch risikofrei.

Reicht es, die Header nur auf der Startseite zu setzen?

Nein. Header wirken pro Antwort. Sie gehören in die Serverkonfiguration, damit jede Seite und jede Datei sie mitliefert — einschließlich der Fehlerseiten.

Was ist der Unterschied zwischen X-Frame-Options und frame-ancestors?

Beide verhindern, dass deine Seite in einen fremden Rahmen eingebettet wird. frame-ancestors ist die modernere Variante innerhalb der CSP und kann mehrere erlaubte Quellen nennen; X-Frame-Options ist der ältere, gröbere Header. Setze beide: den alten für alte Browser, den neuen für alles andere.

Wie mache ich HSTS wieder rückgängig?

Indem du max-age=0 auslieferst und wartest. Der Browser vergisst die Vorgabe erst, wenn er die Seite noch einmal aufruft und den neuen Wert sieht. Bei preload geht es nur über eine Abmeldung bei der Vorlageliste, und die dauert Monate — deshalb die Warnung oben.

Zählt ein JSON-LD-Block als Inline-Skript?

Nein. <script type="application/ld+json"> enthält Daten, keinen ausführbaren Code, und wird von script-src nicht erfasst. Strukturierte Daten für Suchmaschinen können also bleiben, auch bei strenger CSP.

Muss ich die Header im CDN oder auf dem Server setzen?

Auf dem Server. Wird nur im CDN gesetzt, fehlt der Schutz überall dort, wo das CDN umgangen wird — und ein Ausfall des Dienstes nimmt ihn gleich mit. Das CDN darf ergänzen, nicht ersetzen.

In welchem Rahmen solche Maßnahmen zur Pflicht werden, steht im NIS2-Ratgeber.

Prüf deine Website in 30 Sekunden

Ein Scan pro Tag ist kostenlos — ohne Anmeldung, ohne Installation.

Stand: 6. August 2026. Dieser Text erklärt technische Zusammenhänge und ist keine Rechtsberatung.