Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung WordPress 30.09.2026 · 10 min Lesezeit

Sicherheitsheader für WordPress setzen: CSP, HSTS und X-Frame-Options

Welche Sicherheitsheader WordPress selbst sendet und wie Sie X-Content-Type-Options, Referrer-Policy, Permissions-Policy, X-Frame-Options, HSTS und eine Content Security Policy per .htaccess, nginx oder Must-Use-Plugin ergänzen.

Mit KI erstellt – redaktionelle Prüfung ausstehend

WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Mehr dazu

Grafik mit der Überschrift Sicherheitsheader richtig setzen, drei Karten CSP, HSTS, Frame und einem stilisierten WordPress-Adminbereich

Sicherheitsheader sind Anweisungen, die der Webserver bei jeder Antwort an den Browser mitschickt: Lade diese Seite nur per HTTPS, zeige sie nicht in fremden Frames, rate keine Dateitypen, gib beim Klick auf externe Links nur die Domain weiter. Sie ersetzen keine Updates, schließen aber ganze Klassen von Angriffen wie Clickjacking oder das Abgreifen über unverschlüsselte Verbindungen aus. WordPress setzt einige davon bereits selbst, allerdings nur im Adminbereich und auf der Login-Seite. Diese Anleitung zeigt, welche Header WordPress mitbringt, wie Sie die übrigen per .htaccess, nginx oder PHP ergänzen, warum Sie mit der Content Security Policy (CSP) vorsichtig beginnen sollten und wie Sie das Ergebnis prüfen.

Voraussetzungen

  • WordPress: eine aktuelle Installation, getestet mit WordPress 7.1.2.
  • PHP: eine von WordPress unterstützte Version (Mindestempfehlung laut Versions-API PHP 7.4, im Test PHP 8.4). Nur für den PHP-Weg in Schritt 6 relevant.
  • Webserver: Apache mit dem Modul mod_headers (im Test Apache 2.4.68) oder nginx mit Zugriff auf die Server-Konfiguration.
  • Zugriff: FTP, SFTP oder Dateimanager des Hosters für die .htaccess, alternativ SSH. Benutzerkonto mit der Rolle Administrator für die Kontrolle im Backend.
  • HTTPS: Die Website muss vollständig per HTTPS erreichbar sein, bevor Sie HSTS setzen.
  • Backup: eine Kopie der aktuellen .htaccess und ein aktuelles Backup der Website. Ein Tippfehler in der .htaccess führt sofort zu einem Serverfehler.

Schritt 1: Ist-Zustand prüfen

Bevor Sie etwas ändern, sehen Sie nach, was bereits gesendet wird. Viele Hoster setzen Header zentral, und doppelte Header mit widersprüchlichen Werten machen die Fehlersuche schwer.

curl -sI https://www.example.com/
curl -sI https://www.example.com/wp-login.php

Im Labor lieferte die Startseite einer frischen WordPress-Installation keinen einzigen Sicherheitsheader. Die Login-Seite dagegen antwortete mit:

HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self';
Referrer-Policy: strict-origin-when-cross-origin

Diese Header stammen aus WordPress selbst. Die Funktion send_frame_options_header() setzt X-Frame-Options und die CSP-Direktive frame-ancestors, wp_admin_headers() die Referrer-Policy. Beide hängen an den Hooks für Login und Adminbereich, nicht am Frontend. Ihre öffentlichen Seiten sind also ungeschützt, bis Sie selbst Header ergänzen.

Verifizieren: Sie haben die Ausgabe beider Aufrufe gespeichert und wissen, welche Header bereits vom Hoster oder von WordPress kommen.

Schritt 2: Die Header und ihre Wirkung verstehen

Nicht jeder Header ist für jede Website gleich sinnvoll. Die folgende Übersicht nennt die gängigen Header, einen zurückhaltenden Startwert und das Risiko bei falscher Einstellung:

HeaderSchützt vorStartwertRisiko
X-Content-Type-OptionsErraten von Dateitypen (MIME-Sniffing)nosniffgering
X-Frame-OptionsEinbetten in fremde Seiten (Clickjacking)SAMEORIGINgering, stört gewollte Einbettungen
Referrer-PolicyWeitergabe vollständiger URLs an fremde Seitenstrict-origin-when-cross-origingering
Permissions-PolicyZugriff auf Kamera, Mikrofon, Standortcamera=(), microphone=(), geolocation=()mittel, falls Sie Karten oder Videochat nutzen
Strict-Transport-Security (HSTS)Abgreifen über unverschlüsseltes HTTPmax-age=300, später längerhoch bei HTTPS-Problemen
Content-Security-Policyeingeschleuste Skripte (XSS)zuerst nur Report-Onlyhoch, bricht Funktionen

X-Frame-Options gilt als älterer Weg. Moderne Browser werten die CSP-Direktive frame-ancestors aus, die WordPress im Adminbereich zusätzlich sendet. Beide parallel zu setzen schadet nicht und deckt ältere Browser mit ab.

Verifizieren: Sie haben für jeden Header entschieden, ob und mit welchem Wert Sie ihn setzen. Nutzt Ihre Website eingebettete Karten, Videos oder einen Buchungskalender in einem Frame, ist das in der Liste vermerkt.

Schritt 3: Header auf Apache per .htaccess setzen

Die Header-Anweisung gehört zum Apache-Modul mod_headers. Fehlt das Modul, führt die Anweisung zu einem internen Serverfehler (HTTP 500), im Labor ohne Umweg: Eine einzelne Header-Zeile ohne aktives Modul brachte die ganze Website zum Stillstand. Der Block <IfModule mod_headers.c> verhindert das, weil Apache den Inhalt dann schlicht überspringt.

Sichern Sie die vorhandene .htaccess und setzen Sie den folgenden Block oberhalb von # BEGIN WordPress. Den Bereich zwischen # BEGIN WordPress und # END WordPress überschreibt WordPress bei Änderungen an den Permalinks, eigene Zeilen gehören deshalb außerhalb.

<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set Referrer-Policy "strict-origin-when-cross-origin"
Header set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>

Ein Detail aus dem Test: Mit Header always set X-Frame-Options erschien der Header auf wp-login.php doppelt, weil WordPress ihn dort zusätzlich per PHP sendet. Apache verwaltet always und die normale Header-Tabelle getrennt. Ohne always ersetzt Apache den Wert von WordPress, der Header erscheint einmal. Verwenden Sie deshalb Header set ohne always, solange Sie nicht gezielt auch Fehlerseiten abdecken wollen.

Verifizieren: curl -sI https://www.example.com/ zeigt HTTP/1.1 200 OK und die vier neuen Header. curl -sI https://www.example.com/wp-login.php zeigt X-Frame-Options genau einmal. Erscheint ein Fehler 500, stellen Sie die gesicherte .htaccess wieder her.

Schritt 4: HSTS vorsichtig aktivieren

HSTS (RFC 6797) weist den Browser an, die Domain für die angegebene Zeit nur noch per HTTPS aufzurufen. Das verhindert, dass ein Angreifer im selben WLAN die erste unverschlüsselte Anfrage abfängt. Der Haken: Läuft das Zertifikat ab oder bricht HTTPS aus anderem Grund, kommen Besucher mit gespeichertem HSTS nicht mehr auf die Seite, auch nicht mit einer Ausnahme im Browser. Laut RFC darf der Header nur über HTTPS gesendet werden, über HTTP ignorieren Browser ihn.

Beginnen Sie deshalb mit fünf Minuten und erhöhen Sie schrittweise, wenn alles stabil läuft:

<IfModule mod_headers.c>
Header set Strict-Transport-Security "max-age=300" "expr=%{HTTPS} == 'on'"
</IfModule>

Die Bedingung expr=%{HTTPS} == 'on' sorgt dafür, dass Apache den Header nur bei verschlüsselten Antworten setzt. Im Labor enthielt die HTTPS-Antwort Strict-Transport-Security: max-age=31536000 (bei einem Jahr als Testwert), die HTTP-Antwort keinen HSTS-Header. Nach einigen Tagen ohne Probleme setzen Sie max-age=31536000 für ein Jahr. Den Zusatz includeSubDomains ergänzen Sie nur, wenn wirklich alle Subdomains per HTTPS erreichbar sind, auch interne wie ein Webmail oder ein älteres Kundenportal.

Verifizieren: curl -sI https://www.example.com/ | grep -i strict zeigt den Header, curl -sI http://www.example.com/ | grep -i strict zeigt nichts oder nur die Weiterleitung auf HTTPS.

Schritt 5: Content Security Policy im Beobachtungsmodus starten

Die CSP legt fest, aus welchen Quellen der Browser Skripte, Styles, Bilder und Frames laden darf. Richtig gesetzt, entschärft sie viele XSS-Lücken. Falsch gesetzt, fehlen Formulare, Karten, Statistiken oder der Cookie-Banner. Schon eine frische WordPress-7.1-Installation enthielt im Labor auf der Startseite Inline-Skripte, etwa die Import-Map für Skript-Module und die Emoji-Einstellungen. Eine strenge Richtlinie ohne 'unsafe-inline' würde diese blockieren.

Setzen Sie die CSP deshalb zuerst als Content-Security-Policy-Report-Only. Der Browser blockiert dann nichts, meldet Verstöße aber in der Entwicklerkonsole:

<IfModule mod_headers.c>
Header set Content-Security-Policy-Report-Only "default-src 'self'; img-src 'self' data:; frame-ancestors 'self'"
</IfModule>

Öffnen Sie anschließend die wichtigsten Seiten mit geöffneter Entwicklerkonsole (Taste F12, Reiter „Konsole“) und notieren Sie jede gemeldete Quelle: Schriftdienste, Statistik, Zahlungsanbieter, Video-Plattformen. Diese Domains nehmen Sie gezielt in die passenden Direktiven auf, etwa script-src oder frame-src. Erst wenn die Konsole bei allen typischen Abläufen ruhig bleibt, ändern Sie den Headernamen auf Content-Security-Policy.

Trade-off: Eine CSP, die 'unsafe-inline' für Skripte erlaubt, ist leichter einzuführen, schützt aber deutlich weniger. Für viele kleine Websites ist schon frame-ancestors 'self' plus eine Liste erlaubter Domains ein realistischer, wartbarer Stand.

Eine CSP ist nie fertig: Jedes neue Plugin, jeder neue Einbettungscode und manches Update ändert die benötigten Quellen. Wer das zusammen mit den Updates nicht selbst im Blick behalten möchte, kann die laufende Pflege abgeben: Die WordPress-Wartung von wordpressupdate übernimmt Updates von WordPress, Plugins und Themes mit Kompatibilitätsprüfung sowie Einrichtung und Wartung einer Firewall.

Verifizieren: curl -sI zeigt Content-Security-Policy-Report-Only mit Ihrer Richtlinie. Die Website funktioniert unverändert, die Konsole listet die Verstöße, die Sie abarbeiten.

Schritt 6: Alternativen für nginx und für Webspaces ohne .htaccess

nginx: nginx liest keine .htaccess. Die Header setzen Sie im server-Block der Website mit add_header. Der Parameter always sorgt laut nginx-Dokumentation dafür, dass der Header auch bei Fehlerantworten gesendet wird:

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 Strict-Transport-Security "max-age=300" always;

Beachten Sie die Vererbungsregel aus der nginx-Dokumentation: add_header-Anweisungen werden nur dann aus der übergeordneten Ebene übernommen, wenn auf der aktuellen Ebene keine eigenen add_header stehen. Setzt ein location-Block etwa einen Cache-Header, fehlen dort alle Sicherheitsheader aus dem server-Block. Ab nginx 1.29.3 lässt sich dieses Verhalten mit der Anweisung add_header_inherit ändern. Prüfen Sie danach mit nginx -t und laden Sie die Konfiguration neu. Den HSTS-Header setzen Sie nur im HTTPS-server-Block.

PHP über ein Must-Use-Plugin: Ohne Zugriff auf die Server-Konfiguration setzen Sie Header aus WordPress heraus. Der Hook send_headers feuert laut Entwickler-Referenz, nachdem WordPress seine HTTP-Header gesendet hat. Legen Sie die Datei wp-content/mu-plugins/sicherheitsheader.php an:

<?php
/* Plugin Name: Sicherheitsheader */
add_action( 'send_headers', function () {
	header( 'X-Content-Type-Options: nosniff' );
	header( 'Referrer-Policy: strict-origin-when-cross-origin' );
	header( 'Permissions-Policy: camera=(), microphone=(), geolocation=()' );
} );

Must-Use-Plugins lädt WordPress automatisch, sie lassen sich im Backend nicht versehentlich deaktivieren. Der Nachteil: Die Header gelten nur für Anfragen, die WordPress verarbeitet. Bilder, CSS und JavaScript liefert der Webserver direkt aus, im Labor trug ein direkt abgerufenes Bild aus wp-includes/images keinen der Header.

Verifizieren: Bei nginx meldet nginx -t syntax is ok, und curl -sI zeigt die Header auch auf Unterseiten. Beim Must-Use-Plugin erscheint es unter „Plugins > Installierte Plugins“ im Bereich „Must-Use“, und curl -sI auf die Startseite zeigt die Header.

Typische Fehler

  • Fehler 500 nach Änderung der .htaccess: Meist fehlt mod_headers oder eine Zeile enthält einen Tippfehler. Apache meldet im Fehlerprotokoll bei fehlendem Modul Invalid command 'Header', perhaps misspelled or defined by a module not included in the server configuration. Abhilfe: <IfModule mod_headers.c> verwenden und die gesicherte Datei zurückspielen.
  • Header doppelt: Hoster, Plugin und .htaccess setzen denselben Header. Browser verhalten sich bei widersprüchlichen Werten unterschiedlich. Legen Sie eine Stelle fest und entfernen Sie die anderen.
  • Einbettungen verschwinden: Nach X-Frame-Options: SAMEORIGIN lässt sich Ihre Seite nicht mehr in fremden Seiten einbetten. Das ist gewollt. Brauchen Sie eine Einbettung bei einem Partner, erlauben Sie dessen Domain gezielt über frame-ancestors.
  • Website nach HSTS unerreichbar: Abgelaufenes Zertifikat bei langer max-age. Den Eintrag im Browser können Besucher kaum selbst entfernen. Deshalb mit kurzer Laufzeit beginnen und die Zertifikatsverlängerung überwachen.
  • CSP blockiert Funktionen: Formular, Karte oder Cookie-Banner fehlen nach Umstellung von Report-Only auf die echte Richtlinie. Zurück auf Report-Only, Konsole auswerten, fehlende Quellen ergänzen.

Häufige Fragen

Brauche ich dafür ein Sicherheits-Plugin?

Nein. Die Header sind wenige Zeilen Konfiguration. Ein Plugin kann die Pflege erleichtern, ist aber eine weitere Erweiterung, die Updates braucht. Auf dem Webserver gesetzt, gelten die Header auch für statische Dateien.

Ist X-XSS-Protection noch sinnvoll?

Dieser Header steuerte einen Filter älterer Browser, den aktuelle Browser nicht mehr verwenden. Den Schutz vor eingeschleusten Skripten übernimmt heute die CSP. Diese Anleitung setzt ihn deshalb nicht.

Wie prüfe ich die Header ohne Kommandozeile?

Öffnen Sie im Browser die Entwicklerwerkzeuge (F12), wechseln Sie zum Reiter „Netzwerk“, laden Sie die Seite neu und klicken Sie auf die erste Anfrage. Im Bereich der Antwort-Header stehen alle gesendeten Header.

Testumfang

Wir haben alle Apache-Beispiele dieser Anleitung in einer Testumgebung mit WordPress 7.1.2 ausprobiert. Auffällig war, dass die Seite mit Fehler 500 ausfällt, wenn mod_headers fehlt. Die nginx-Konfiguration haben wir nicht selbst getestet, sondern nach der Dokumentation beschrieben. Auch eine produktive CSP mit externen Diensten haben wir nicht geprüft, deshalb sollten Sie Ihre Richtlinie zuerst auf einer Kopie Ihrer Website testen.

Fazit

WordPress schützt mit eigenen Headern nur Login und Adminbereich. Mit wenigen Zeilen in der .htaccess, im nginx-server-Block oder in einem Must-Use-Plugin erhalten auch Ihre öffentlichen Seiten X-Content-Type-Options, Referrer-Policy, Permissions-Policy und einen Frame-Schutz. HSTS und CSP bringen mehr Schutz, verlangen aber ein schrittweises Vorgehen mit kurzer Laufzeit beziehungsweise Beobachtungsmodus. Wer Updates und Firewall dauerhaft abgeben möchte, findet bei der WordPress-Wartung von wordpressupdate persönliche Betreuung dafür.

Weiterführende Anleitungen und Quellen

WordPressSicherheitHTTP-HeaderApachenginxHSTS