Sechs HTTP-Response-Header entscheiden mit darüber, wie viel Schaden eine Lücke in einem Plugin oder Theme anrichten kann. Sie werden nicht in WordPress gesetzt, sondern im Webserver. Diese Anleitung erklärt jeden Header und liefert fertige Konfigurationsblöcke für Apache und nginx.
Unser Plugin Security Check Report prüft in der Gruppe Netzwerk und Transport, welche der empfohlenen Security-Header Ihre Site sendet. Sechs Header fließen gewichtet in die Bewertung ein: Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, Referrer-Policy und Permissions-Policy. Drei weitere (die Cross-Origin-Header COOP, COEP und CORP) meldet das Plugin nur als optional, weil sie auf gewöhnlichen Websites eingebettete Inhalte wie YouTube-Videos zerstören können.
Wichtig für die Einordnung: Diese Header verhindern keinen Einbruch in den Server. Sie sind Anweisungen an den Browser Ihrer Besucher und begrenzen, was eine Schwachstelle an anderer Stelle anrichten kann. Genau deshalb gehören sie in die Webserver-Konfiguration und nicht in ein WordPress-Plugin: Nur dort gelten sie auch für statische Dateien wie Bilder, CSS und JavaScript, die WordPress gar nicht ausliefert.
Was jeder Header konkret verhindert
X-Content-Type-Options: nosniff
Ohne diesen Header darf der Browser raten, was für eine Datei er gerade bekommt (MIME-Sniffing). Eine als Bild hochgeladene Datei, die in Wahrheit JavaScript enthält, kann so trotzdem als Skript ausgeführt werden. Mit nosniff gilt ausschließlich der Content-Type, den der Server angibt. Der Header hat keinen bekannten Nebeneffekt auf normalen Websites und gehört auf jede Site, ausnahmslos.
X-Frame-Options: SAMEORIGIN
Verhindert, dass fremde Seiten Ihre Site in einen unsichtbaren iframe laden. Das ist die Grundlage für Clickjacking: Ein Besucher glaubt, auf der fremden Seite einen Button zu klicken, klickt aber in Wahrheit in Ihrem eingebetteten Admin-Bereich, etwa auf „Benutzer anlegen“. SAMEORIGIN erlaubt das Einbetten nur innerhalb der eigenen Domain. Der modernere Weg ist die Direktive frame-ancestors in der Content-Security-Policy; sendet Ihre Site diese, wertet auch unser Plugin einen fehlenden X-Frame-Options-Header nicht mehr als Mangel.
Referrer-Policy: strict-origin-when-cross-origin
Steuert, wie viel von der aktuellen URL beim Klick auf einen externen Link mitgeschickt wird. Ohne Policy kann die komplette URL übertragen werden, inklusive Pfaden wie /mein-konto/bestellung/4711/ oder Token in Query-Parametern, etwa aus einem Passwort-Reset-Link. Mit strict-origin-when-cross-origin erhält die fremde Site nur noch https://ihre-domain.de/, und bei einem Wechsel von HTTPS zu HTTP gar nichts. Moderne Browser nutzen diesen Wert inzwischen als Voreinstellung; der Header stellt sicher, dass er auch für ältere Browser gilt.
Content-Security-Policy
Der mächtigste und zugleich heikelste Header: eine Positivliste, welche Quellen der Browser für Skripte, Styles, Bilder und Frames akzeptiert. Eine strikte Policy macht eingeschleustes JavaScript (XSS) weitgehend wirkungslos, weil der Browser Code aus nicht gelisteten Quellen verweigert. Auf einer typischen WordPress-Site mit Theme, Page-Builder und Analytics ist eine vollständige Policy allerdings Detailarbeit, denn eine zu strenge Regel legt sofort sichtbare Teile der Site lahm. Der praxistaugliche Einstieg: zuerst nur frame-ancestors 'self' erzwingen (das ersetzt X-Frame-Options und bricht nichts), die vollständige Policy zunächst als Content-Security-Policy-Report-Only ausliefern und erst scharf schalten, wenn die Verstoßberichte still sind. Eine Policy mit unsafe-inline erlaubt genau das, was sie verhindern soll; unser Plugin bewertet sie deshalb gesondert.
Permissions-Policy
Schaltet Browser-Funktionen wie Kamera, Mikrofon und Standortabfrage für Ihre Site ab. Braucht Ihre Site keine dieser Funktionen (bei fast allen WordPress-Sites der Fall), kann eingeschleustes Skript sie dann auch nicht im Namen Ihrer Domain anfordern. Der Besucher sieht keine irritierende Berechtigungsabfrage, die scheinbar von Ihrer Site stammt.
Strict-Transport-Security (HSTS), nur kurz
HSTS weist den Browser an, Ihre Domain für die angegebene Dauer ausschließlich per HTTPS anzusteuern. Das Plugin prüft dabei nicht nur die Existenz, sondern auch, ob die max-age lang genug ist: ein Wert von wenigen Minuten besteht jeden Existenz-Check und schützt niemanden. Weil ein falsch gesetzter HSTS-Header eine Site für Monate unerreichbar machen kann, behandeln wir die schrittweise Einführung im eigenen Doku-Artikel HSTS schrittweise einführen und lassen ihn in den folgenden Blöcken bewusst weg.
Apache: Header in .htaccess oder vhost setzen
Apache setzt Response-Header über das Modul mod_headers. Auf Shared Hosting ist es praktisch immer aktiv; auf eigenen Servern aktivieren Sie es mit a2enmod headers. Der folgende Block funktioniert in der .htaccess im WordPress-Stammverzeichnis genauso wie im vhost:
<IfModule mod_headers.c>
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 "camera=(), microphone=(), geolocation=()"
Header always set Content-Security-Policy "frame-ancestors 'self'"
</IfModule>
Das Schlüsselwort always sorgt dafür, dass die Header auch bei Fehlerseiten wie 404 oder 500 gesendet werden; ohne always fehlen sie dort. Der IfModule-Wrapper verhindert einen Serverfehler, falls mod_headers doch nicht geladen ist. In der .htaccess gehört der Block außerhalb der von WordPress verwalteten # BEGIN WordPress-Markierungen, sonst wird er beim nächsten Permalink-Update überschrieben.
nginx: add_header im server-Block
nginx wertet keine .htaccess-Dateien aus. Die Header gehören in den server-Block Ihrer Site, je nach Distribution unter /etc/nginx/sites-available/ oder /etc/nginx/conf.d/:
server {
# ... bestehende Konfiguration ...
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 Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "frame-ancestors 'self'" always;
}
Der Parameter always hat dieselbe Aufgabe wie bei Apache: Ohne ihn sendet nginx die Header nur bei bestimmten Status-Codes wie 200 und 301, nicht aber bei 404 oder 500.
Eine Falle sollten Sie kennen, denn sie ist der häufigste Grund für scheinbar verschwundene Header: add_header-Anweisungen werden nur dann aus dem server-Block geerbt, wenn der jeweilige location-Block selbst keine einzige add_header-Zeile enthält. Sobald ein location-Block auch nur einen eigenen Header setzt (typisch: Cache-Control für statische Dateien), verwirft nginx dort sämtliche Header aus dem server-Block. So dokumentiert es die offizielle nginx-Referenz. Die robuste Lösung: alle Security-Header in eine Datei /etc/nginx/snippets/security-headers.conf auslagern und in jedem betroffenen location-Block per include snippets/security-headers.conf; erneut einbinden. Danach wie immer:
nginx -t
systemctl reload nginx
So testen Sie das Ergebnis
Prüfen Sie nach dem Neuladen, was der Server tatsächlich sendet. Auf der Kommandozeile genügt ein Request auf die Startseite:
curl -sI https://ihre-domain.de/ | grep -iE
"strict-transport|content-security|x-content-type|x-frame|referrer-policy|permissions-policy"
Jede der gesetzten Zeilen muss in der Ausgabe erscheinen. Testen Sie zusätzlich eine statische Datei (etwa ein Bild aus /wp-content/uploads/) und eine nicht existierende URL, um die 404-Antwort zu sehen: Fehlen die Header dort, haben Sie bei nginx die location-Vererbung oder bei Apache das fehlende always gefunden. Denselben Abgleich erledigt der Security Check Report bei jedem Lauf automatisch mit: Fehlende Header stehen einzeln im Befund, und ist die Prüfung mangels Antwort nicht möglich, meldet das Plugin „nicht feststellbar“ statt eines Fehlalarms.
Häufige Fragen
Kann ich die Header nicht einfach per Plugin setzen?
Es gibt WordPress-Plugins, die Header über PHP senden. Sie greifen aber nur bei Seiten, die durch WordPress laufen. Bilder, CSS- und JavaScript-Dateien liefert der Webserver direkt aus, ohne PHP, und bleiben ohne Header. Damit die Header für alle Antworten gelten, führt am Webserver kein Weg vorbei. Genau deshalb empfiehlt auch unser Plugin im Befundtext, die Header serverseitig zu setzen.
Kann einer dieser Header meine Site kaputt machen?
X-Content-Type-Options und Referrer-Policy können Sie gefahrlos sofort setzen. Bei X-Frame-Options mit SAMEORIGIN und frame-ancestors 'self' gibt es nur dann Nebenwirkungen, wenn Ihre Site absichtlich auf fremden Domains eingebettet wird. Vorsicht ist bei zwei Kandidaten geboten: einer vollständigen Content-Security-Policy (immer erst im Report-Only-Modus testen) und HSTS (falsch gesetzt für Monate bindend).
Warum meldet das Plugin fehlende Header, obwohl ich sie gesetzt habe?
Die Prüfung ruft Ihre Startseite auf und liest die tatsächlich gesendeten Response-Header. Meldet sie einen Header als fehlend, kommt er dort nicht an. Häufigste Ursachen: die nginx-location-Vererbung aus dieser Anleitung, ein nicht neu geladener Webserver, ein davorliegender Proxy oder ein Cache, der die Header verschluckt. Der curl -sI-Test von oben zeigt Ihnen in Sekunden, was wirklich ausgeliefert wird.
Muss ich auch die Cross-Origin-Header COOP, COEP und CORP setzen?
Für gewöhnliche WordPress-Sites: nein. Diese drei Header isolieren eine Site vollständig von fremden Ursprüngen und zerbrechen dabei schnell eingebettete Inhalte wie YouTube-Videos oder Kartendienste. Der Security Check Report führt sie deshalb nur als optionalen Hinweis, nicht als Mangel.
Weitere Härtungsmaßnahmen rund um WordPress finden Sie in unserer Übersicht WordPress-Sicherheit. Und ob Ihre Site die Header bereits sendet, sagt Ihnen ein Lauf des kostenlosen Security Check Report: 60 Prüfungen, Note von A bis F, und ganz oben die Liste dessen, was zuerst dran ist. Der Quelltext ist öffentlich auf GitHub einsehbar.