Security Headers: So sicherst du deine Website in 20 Minuten ab
Es gibt in der Web-Sicherheit nicht viele Dinge, die so wenig Aufwand kosten und so viel bringen wie Security Headers. Du fügst ein paar Zeilen zu deiner Server-Konfiguration hinzu, deployst einmal, und schon bekommt der Browser klare Anweisungen, wie er deine Seite schützen soll. Keine neue Library, kein Umbau am Code, keine Abhängigkeit, die du pflegen musst.
Trotzdem fehlen sie auf erstaunlich vielen Seiten. Oft, weil das Thema technisch klingt und man nicht so recht weiß, wo man anfangen soll. Genau das ändern wir hier. Wir schauen uns an, was Security Headers eigentlich machen, welche sechs wirklich zählen, wie du sie auf gängigen Plattformen setzt und welche Stolperfallen es gibt. Am Ende hast du eine Checkliste zum Abhaken und bringst dein Rating bei securityheaders.com von einem F auf ein A oder A+.
Eine Sache vorweg, damit keine falschen Erwartungen entstehen: Security Headers sind eine Schutzschicht, nicht die ganze Burg. Sie ersetzen keine saubere Eingabe-Validierung, kein vernünftiges Session-Management und kein Patchen deiner Abhängigkeiten. Sie machen es einem Angreifer aber spürbar schwerer und fangen eine ganze Reihe klassischer Angriffe ab, bevor sie überhaupt wirken. Genau das ist das Prinzip von Defense in Depth: viele Schichten, die zusammen mehr leisten als jede einzelne.
Was Security Headers eigentlich sind
Jedes Mal, wenn dein Server eine Antwort an den Browser schickt, kommen darin nicht nur dein HTML und deine Bilder, sondern auch eine Reihe von Metadaten: die HTTP-Response-Header. Die meisten kennst du wahrscheinlich, ohne groß darüber nachzudenken, zum Beispiel Content-Type oder Cache-Control.
Security Headers sind eine besondere Gruppe davon. Sie geben dem Browser Sicherheitsregeln mit. Der Browser ist hier dein Verbündeter: Er setzt die Regeln durch, noch bevor irgendein Schaden entsteht. Ein Beispiel macht das greifbar. Mit einem einzigen Header sagst du dem Browser sinngemäß: “Lade Skripte nur von meiner eigenen Domain.” Versucht ein Angreifer dann, über eine Lücke fremden JavaScript-Code einzuschleusen, verweigert der Browser die Ausführung. Der Angriff läuft ins Leere, ohne dass du jede einzelne Lücke selbst gefunden haben musst.
Das ist der Grund, warum Security Headers so ein gutes Preis-Leistungs-Verhältnis haben. Du verschiebst einen Teil der Verteidigung in den Browser, und der ist in Sachen Durchsetzung sehr zuverlässig.
Zuerst messen, dann verbessern
Bevor du irgendetwas änderst, lohnt sich ein Blick auf den Ist-Zustand. Das dauert zwei Minuten und du siehst direkt, wo du stehst.
Am schnellsten geht es mit securityheaders.com. Du gibst deine URL ein und bekommst eine Note von F bis A+ plus eine Liste, welche Header fehlen. Viele Seiten starten bei F, einfach weil noch gar nichts gesetzt ist. Das ist kein Drama, sondern dein Ausgangspunkt.
Wer es genauer mag, schaut zusätzlich in die Browser-DevTools. Öffne den Tab “Network”, lade die Seite neu, klick auf das erste Dokument in der Liste und sieh dir unter “Headers” die Response-Header an. Dort steht schwarz auf weiß, was dein Server gerade ausliefert. Eine zweite gute Quelle für eine tiefere Analyse ist der Mozilla HTTP Observatory.
Der Tipp dahinter: Mach diesen Test jetzt, notier dir die Note, und mach ihn nach deinen Änderungen noch einmal. Der Sprung ist motivierend und zeigt dir sofort, ob alles greift.
Die sechs Header, die wirklich zählen
Es gibt mehr als sechs Security Headers, aber diese sechs decken den Großteil ab. Ich gehe sie der Reihe nach durch: was sie tun, warum sie wichtig sind und welchen Wert du setzen kannst.
1. Strict-Transport-Security
Dieser Header, oft HSTS abgekürzt, zwingt den Browser dazu, deine Seite ausschließlich über HTTPS aufzurufen. Hat ein Besucher deine Seite einmal mit HSTS geladen, ruft sein Browser sie für die festgelegte Dauer nie wieder über unverschlüsseltes HTTP auf, selbst wenn er http:// eintippt oder auf einen alten Link klickt. Das schützt vor einer ganzen Klasse von Angriffen, bei denen jemand die Verbindung abfängt und auf HTTP zurückstuft.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadmax-age ist die Gültigkeit in Sekunden, hier ein Jahr. includeSubDomains dehnt die Regel auf alle Subdomains aus. preload meldet deine Domain für die Vorab-Liste der Browser an.
Eine Warnung dazu, weil HSTS einer der wenigen Header ist, mit denen du dir kurzfristig die Seite lahmlegen kannst: Setz includeSubDomains und preload erst, wenn wirklich jede Subdomain sauber über HTTPS läuft. preload ist außerdem schwer rückgängig zu machen. Wenn du unsicher bist, starte mit einem kleineren max-age ohne preload und steigere dich.
2. Content-Security-Policy
Die Content-Security-Policy, kurz CSP, ist der mächtigste und gleichzeitig anspruchsvollste Header. Sie legt fest, aus welchen Quellen der Browser Inhalte laden und ausführen darf: Skripte, Styles, Bilder, Schriften, Frames und mehr. Richtig eingesetzt ist sie deine stärkste Waffe gegen Cross-Site-Scripting, weil eingeschleuste Skripte aus nicht erlaubten Quellen schlicht nicht ausgeführt werden.
Eine einfache Startrichtlinie sieht so aus:
Content-Security-Policy: default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'default-src 'self' bedeutet: Standardmäßig nur Inhalte von der eigenen Domain. Die weiteren Direktiven verfeinern das pro Ressourcentyp. img-src 'self' data: erlaubt zum Beispiel zusätzlich eingebettete Data-URLs für Bilder.
Der heikle Teil ist script-src. Sobald du dort 'unsafe-inline' erlaubst, also Inline-Skripte und onclick-Attribute im HTML, schwächst du den XSS-Schutz deutlich, denn genau diese Inline-Ausführung nutzen viele Angriffe. Der saubere Weg sind Nonces oder Hashes: Du markierst deine eigenen Skripte mit einem zufälligen Token oder einem Prüfwert, und nur die laufen. Das ist etwas mehr Arbeit und hängt von deinem Setup ab.
Mein dringender Rat: Führe CSP nicht blind im scharfen Modus ein, sonst bricht dir womöglich die halbe Seite. Nutze zuerst den Report-Only-Modus. Dann meldet der Browser Verstöße, blockiert aber nichts:
Content-Security-Policy-Report-Only: default-src 'self'So siehst du in der Konsole, was deine echte Seite alles lädt, kannst die Richtlinie sauber anpassen und schaltest erst dann auf den durchsetzenden Header um. CSP ist eher ein kleiner Prozess als ein Einzeiler, aber es lohnt sich.
3. X-Content-Type-Options
Dieser Header ist ein Einzeiler ohne Nebenwirkungen, den du immer setzen solltest:
X-Content-Type-Options: nosniffEr verbietet dem Browser das sogenannte MIME-Sniffing. Ohne ihn rät der Browser bei unklarem Content-Type manchmal selbst, um was für eine Datei es sich handelt. Das klingt harmlos, ist aber ein Einfallstor: Eine als Bild hochgeladene Datei könnte vom Browser fälschlich als Skript interpretiert und ausgeführt werden. nosniff sagt: Nimm den Content-Type, den ich angebe, und rate nicht.
4. X-Frame-Options
Mit diesem Header steuerst du, ob deine Seite in einem Frame oder iframe auf einer anderen Seite eingebettet werden darf. Das schützt vor Clickjacking, einer Angriffsart, bei der deine Seite unsichtbar über eine fremde gelegt wird, sodass Nutzer auf Dinge klicken, die sie gar nicht sehen.
X-Frame-Options: SAMEORIGINSAMEORIGIN erlaubt das Einbetten nur auf der eigenen Domain. DENY verbietet es komplett. Gut zu wissen: Die moderne Variante davon ist die CSP-Direktive frame-ancestors, die feiner steuerbar ist. X-Frame-Options zusätzlich zu setzen, schadet aber nicht und deckt ältere Browser ab.
5. Referrer-Policy
Wenn ein Besucher von deiner Seite auf eine andere klickt, schickt der Browser standardmäßig die vollständige Herkunfts-URL mit. Das kann mehr verraten, als dir lieb ist, etwa interne Pfade oder Tokens in der URL. Die Referrer-Policy schränkt das ein.
Referrer-Policy: strict-origin-when-cross-originDieser Wert ist ein guter Standard: Innerhalb deiner eigenen Domain wird die volle URL übertragen, bei Sprüngen auf fremde Domains nur noch die nackte Herkunft ohne Pfad, und bei einem Wechsel von HTTPS auf HTTP gar nichts. Für die meisten Seiten ist das genau die richtige Balance.
6. Permissions-Policy
Dieser Header steuert, welche Browser-Funktionen deine Seite und eingebettete Inhalte nutzen dürfen: Kamera, Mikrofon, Standort, Bewegungssensoren und einiges mehr. Wenn deine Seite diese Dinge gar nicht braucht, schalte sie ab. Dann kann auch ein eingeschleustes Skript sie nicht missbrauchen.
Permissions-Policy: camera=(), microphone=(), geolocation=()Die leeren Klammern bedeuten: Diese Funktion ist für niemanden erlaubt, auch nicht für die eigene Seite. Brauchst du eine Funktion selbst, schreibst du geolocation=(self). Diese drei sind nur der Anfang: Schalte alles ab, was du nicht brauchst, etwa payment=(), usb=() oder die Bewegungssensoren accelerometer=() und gyroscope=(). Je weniger erlaubt ist, desto weniger kann ein eingeschleustes Skript anrichten.
Eine kurze Notiz noch zu einem alten Bekannten: X-XSS-Protection. Diesen Header findest du in vielen älteren Anleitungen. Er steuert einen XSS-Filter, den moderne Browser längst nicht mehr verwenden, teilweise konnte er sogar neue Probleme schaffen. Du kannst ihn bedenkenlos weglassen und stattdessen auf eine ordentliche Content-Security-Policy setzen. Falls er bei dir noch gesetzt ist, ist das kein Beinbruch, aber nötig ist er nicht.
Wo du die Header setzt
Das Schöne ist: Du setzt diese Header an genau einer Stelle, zentral, und sie gelten für alle Antworten. Wo diese Stelle liegt, hängt von deinem Hosting ab. Hier die gängigsten Wege.
Vercel
Diese Seite hier läuft auf Vercel, und dort werden die Header in der vercel.json im Projekt-Root definiert. Das ist angenehm, weil alles versioniert im Repo liegt und kein Server-Zugriff nötig ist.
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains; preload"
},
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "SAMEORIGIN" },
{
"key": "Referrer-Policy",
"value": "strict-origin-when-cross-origin"
},
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'"
},
{
"key": "Permissions-Policy",
"value": "camera=(), microphone=(), geolocation=()"
}
]
}
]
}"source": "/(.*)" heißt: für alle Pfade. Du kannst auch gezielt eigene Regeln für bestimmte Bereiche setzen, etwa strengere Header für /api/.
Nginx
Auf einem klassischen Server mit Nginx kommen die Header in den server-Block:
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;
add_header Content-Security-Policy "default-src 'self'" always;Das always am Ende sorgt dafür, dass die Header auch bei Fehlerseiten wie 404 mitgeschickt werden. Das wird gern vergessen.
Express und Node
In einer Node-Anwendung mit Express musst du das Rad nicht neu erfinden. Das Paket Helmet setzt sinnvolle Defaults für fast alle dieser Header mit einer Zeile:
import helmet from "helmet";
app.use(helmet());Helmet aktiviert die wichtigsten Header automatisch und lässt sich danach gezielt anpassen, etwa für eine eigene Content-Security-Policy. Für eigene Server eine sehr bequeme Lösung.
Die häufigsten Stolperfallen
Ein paar Dinge gehen erfahrungsgemäß schief. Wenn du sie kennst, sparst du dir den Frust.
Die erste betrifft die CSP: Schaltest du sie zu streng und sofort scharf, verschwinden plötzlich Bilder, Styles brechen, eingebettete Videos laden nicht mehr. Deshalb der Report-Only-Modus zuerst. Beobachte ein paar Tage, sammle die Verstöße, passe an, dann erst durchsetzen.
Die zweite ist HSTS mit preload und includeSubDomains auf einem Setup, bei dem noch nicht alles über HTTPS läuft. Eine vergessene Subdomain ohne gültiges Zertifikat ist dann für die Dauer von max-age nicht mehr erreichbar. Erst alles auf HTTPS bringen, dann verschärfen.
Die dritte ist das Vergessen von Fehlerseiten. Bei Nginx das always, bei anderen Setups die Frage, ob die Header wirklich für jede Antwort gelten und nicht nur für die Startseite. Prüf das nach dem Deploy mit den DevTools auf einer Unterseite.
Und die vierte, ganz banal: nach dem Deploy nicht nachmessen. Setz die Header, deploye, und teste dann erneut bei securityheaders.com. Manchmal greift eine Regel nicht wie gedacht, und der erneute Test deckt das in Sekunden auf.
Deine Security-Header-Checkliste
Zum Abhaken, in der Reihenfolge, in der ich es empfehle:
- Ist-Zustand bei securityheaders.com gemessen und Note notiert
X-Content-Type-Options: nosniffgesetzt, der risikoloseste Quick-WinX-Frame-Options: SAMEORIGINgegen Clickjacking gesetztReferrer-Policy: strict-origin-when-cross-origingesetztPermissions-Policyfür nicht benötigte Funktionen abgeschaltetStrict-Transport-Securitygesetzt, erst ohnepreload, dann verschärft- Content-Security-Policy zuerst im Report-Only-Modus getestet, dann scharf geschaltet
- Header gelten für alle Pfade und auch für Fehlerseiten
- Nach dem Deploy erneut gemessen, Rating auf A oder A+ geprüft
Wie es weitergeht
Security Headers sind ein hervorragender Einstieg, weil sie schnell wirken und du sofort ein Ergebnis siehst. Aber sie sind, wie gesagt, eine von vielen Schichten. Wer eine Web-App wirklich sicher machen will, schaut sich als Nächstes an, wie Cross-Site-Scripting und SQL-Injection im Detail funktionieren, wie sicheres Session-Management und Password-Hashing aussehen, wie CORS richtig konfiguriert wird und wie man Zugriffskontrolle sauber umsetzt. Diese Themen bauen aufeinander auf, und genau so habe ich sie in meinem Kurs aufbereitet.
Wenn du Web-Sicherheit systematisch und in verdaulichen Tagesportionen lernen möchtest, schau dir den Web Security Mastery Kurs an. In 30 Tagen gehst du vom Security-Mindset über die OWASP Top 10 und die hier gezeigten Header bis hin zu Authentifizierung, API-Sicherheit und einem abschließenden Praxis-Audit deiner eigenen Anwendung.
Web Security Mastery
Für heute reicht es, wenn du die Checkliste durchgehst. Zwanzig Minuten, ein Deploy, und deine Seite ist messbar sicherer als vorher. Das ist ein guter Anfang.
Weitere Blog Artikel
Bleib auf dem Laufenden
Erhalte neue Inhalte direkt in dein Postfach
Was erwartet dich?
Neue Artikel & Videos
Wenn ich was Neues veröffentliche, erfährst du's hier zuerst.
Plattform & Kurs-Updates
Neue Kurse, Referenz-Updates und gelegentlich Sonderpreise zum Launch.
Einmal die Woche in deinem Postfach
Kurz, auf den Punkt, kein Spam.