WordPress mit Fail2ban schützen: wp-login.php und XML-RPC auf Serverebene sperren
So richten Sie auf einem eigenen Linux-Server einen Fail2ban-Filter und eine Jail für WordPress ein: Fehlversuche an wp-login.php und XML-RPC im Access-Log erkennen, IP-Adressen sperren und wieder freigeben.
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

Automatisierte Anmeldeversuche auf wp-login.php und xmlrpc.php gehören zum Alltag jeder öffentlich erreichbaren WordPress-Website. Ein Sicherheits-Plugin kann solche Versuche bremsen, läuft aber selbst in PHP: Jeder Angriff startet WordPress, lädt Plugins und fragt die Datenbank ab. Fail2ban setzt eine Ebene tiefer an. Das Programm liest die Logdatei des Webservers, erkennt wiederholte Fehlversuche einer IP-Adresse und sperrt diese Adresse für eine festgelegte Zeit in der Firewall des Servers. Danach erreichen die Anfragen weder Apache noch PHP. Diese Anleitung richtet für einen eigenen Linux-Server (vServer oder Root-Server mit Debian oder Ubuntu) einen Filter und eine Jail für WordPress ein, testet sie mit fail2ban-regex und zeigt, wie Sie gesperrte Adressen prüfen und wieder freigeben. Filter und Jail wurden mit Fail2ban 1.1.0 unter Debian 13 und WordPress 7.1.2 getestet.
Voraussetzungen
- Server: eigener Linux-Server mit Root-Zugang per SSH, hier Debian 13 (Trixie) oder ein aktuelles Ubuntu. Auf Shared Hosting können Sie weder Pakete installieren noch Firewall-Regeln setzen.
- WordPress: eine aktuelle Version, getestet mit WordPress 7.1.2 auf PHP 8.4.
- Webserver: Apache oder nginx mit Access-Log im üblichen Format „combined“ (Standard bei Debian und Ubuntu). Sie müssen wissen, in welche Datei die Website loggt.
- Firewall: nftables oder iptables auf dem Server. Debian nutzt für Fail2ban standardmäßig nftables.
- Rolle: Administrator in WordPress, um die Anmeldung nach der Einrichtung zu testen.
- Backup und Notzugang: eine aktuelle Sicherung sowie ein zweiter Weg auf den Server (Konsole im Kundenbereich des Hosters), falls Sie sich versehentlich selbst aussperren.
- Ressourcen: Fail2ban ist ein kleiner Python-Dienst, ein Server mit 2 Kernen und 4 GB RAM für WordPress reicht auch dafür.
Schritt 1: Fail2ban installieren und Logdatei finden
Fail2ban liegt in den Paketquellen von Debian und Ubuntu. Debian 13 liefert Version 1.1.0 aus. Installieren Sie das Paket als root:
apt update
apt install fail2ban
fail2ban-client --version
Die Ausgabe im Test lautete Fail2Ban v1.1.0. Das Debian-Paket bringt eine Datei /etc/fail2ban/jail.d/defaults-debian.conf mit, die als Sperraktion nftables setzt und die Jail für SSH aktiviert. Diese SSH-Jail bleibt sinnvoll, Sie ergänzen sie nur.
Wichtiger als die Installation ist die richtige Logdatei. Fail2ban sieht nur, was im Access-Log steht. Bei einer Standardinstallation liegt es unter /var/log/apache2/access.log beziehungsweise /var/log/nginx/access.log. Viele Server schreiben je Website eine eigene Datei, etwa /var/log/apache2/firma-access.log. Den Pfad finden Sie in der Konfiguration des virtuellen Hosts (CustomLog bei Apache, access_log bei nginx). Lösen Sie dann eine fehlgeschlagene Anmeldung aus und sehen Sie nach:
grep -h 'wp-login.php\|xmlrpc.php' /var/log/apache2/*access.log | tail -5
Im Labor sahen die Zeilen so aus (eine falsche Anmeldung, eine richtige, ein Aufruf der Anmeldeseite, ein XML-RPC-Anmeldeversuch):
127.0.0.1 - - [30/Sep/2026:19:40:47 +0000] "POST /wp-login.php HTTP/1.1" 200 10053 "-" "curl/8.14.1"
127.0.0.1 - - [30/Sep/2026:19:40:48 +0000] "POST /wp-login.php HTTP/1.1" 302 1214 "-" "curl/8.14.1"
127.0.0.1 - - [30/Sep/2026:19:40:49 +0000] "GET /wp-login.php HTTP/1.1" 200 9631 "-" "curl/8.14.1"
127.0.0.1 - - [30/Sep/2026:19:40:49 +0000] "POST /xmlrpc.php HTTP/1.1" 200 634 "-" "curl/8.14.1"
Das ist der Kern des Verfahrens: Bei falschem Passwort zeigt WordPress die Anmeldeseite erneut mit Fehlermeldung an, Status 200. Bei richtigem Passwort leitet WordPress mit Status 302 ins Dashboard weiter. XML-RPC antwortet dagegen immer mit 200, auch bei einem Fehler. Die Fehlermeldung steht nur im Inhalt der Antwort (faultCode 403, „Der Benutzername oder das Passwort ist falsch.“), den das Access-Log nicht enthält.
Verifizieren: fail2ban-client --version gibt eine Version aus, und in der Logdatei Ihrer Website erscheinen nach einem Fehlversuch Zeilen mit "POST /wp-login.php ..." 200 und der echten IP-Adresse Ihres Anschlusses.
Schritt 2: Echte Besucher-IP sicherstellen
Steht in der ersten Spalte jeder Zeile dieselbe Adresse, etwa die eines Load Balancers, Reverse Proxys oder CDN, dürfen Sie hier nicht weitermachen. Fail2ban würde nach wenigen Fehlversuchen den Proxy sperren und damit alle Besucher. Der Webserver muss die Adresse aus dem Header des Proxys übernehmen. Bei Apache erledigt das mod_remoteip mit RemoteIPHeader und RemoteIPTrustedProxy, bei nginx das Modul ngx_http_realip_module mit set_real_ip_from und real_ip_header. Tragen Sie dabei nur die Adressbereiche Ihres Proxys als vertrauenswürdig ein, sonst kann jeder Angreifer eine beliebige IP-Adresse im Header mitschicken.
Ein zweiter Punkt: Bei einem CDN endet die Verbindung des Angreifers am CDN, nicht an Ihrem Server. Eine Sperre in der lokalen Firewall trifft dann nur die Verbindung vom CDN zu Ihnen. In diesem Aufbau ist eine Begrenzung direkt beim CDN der passendere Ort, Fail2ban bringt dort wenig.
Verifizieren: Rufen Sie die Website von einem anderen Anschluss (etwa über das Mobilnetz) auf. Im Access-Log steht diese öffentliche Adresse, nicht die Adresse des Proxys.
Schritt 3: Filter für wp-login.php und XML-RPC anlegen
Fail2ban bringt Filter für viele Dienste mit, aber keinen für die WordPress-Anmeldung. Legen Sie einen eigenen Filter an. Eigene Dateien in filter.d und jail.d überstehen Paketupdates, Änderungen an jail.conf dagegen nicht zuverlässig.
nano /etc/fail2ban/filter.d/wordpress-login.conf
[Definition]
failregex = ^<HOST> -[^"]*"POST /wp-login\.php[^"]*" 200\b
^<HOST> -[^"]*"POST /xmlrpc\.php[^"]*" 200\b
ignoreregex =
<HOST> ist der Platzhalter, aus dem Fail2ban die IP-Adresse liest. Die erste Zeile zählt jeden POST auf wp-login.php mit Status 200, also jede gescheiterte Anmeldung. Die zweite Zeile zählt jeden POST auf xmlrpc.php. Das Datum erkennt Fail2ban im Format „combined“ selbst, eine eigene datepattern-Zeile ist nicht nötig.
Der Trade-off bei XML-RPC: Weil das Log erfolgreiche und gescheiterte Aufrufe nicht unterscheidet, zählt auch legitimer Verkehr. Nutzen Sie die WordPress-App, Jetpack oder andere Dienste, die über XML-RPC arbeiten, lassen Sie die zweite Zeile weg oder setzen Sie maxretry deutlich höher. Brauchen Sie XML-RPC gar nicht, ist das Abschalten der sauberere Weg, siehe XML-RPC deaktivieren oder einschränken.
Prüfen Sie den Filter gegen Ihr echtes Log, bevor Sie ihn scharf schalten:
fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/wordpress-login.conf
Im Test mit den vier Beispielzeilen oben und zwei weiteren meldete das Programm:
Failregex: 3 total
| 1) [2] ^<HOST> -[^"]*"POST /wp-login\.php[^"]*" 200\b
| 2) [1] ^<HOST> -[^"]*"POST /xmlrpc\.php[^"]*" 200\b
Lines: 6 lines, 0 ignored, 3 matched, 3 missed
Die erfolgreiche Anmeldung (302), der reine Aufruf der Anmeldeseite (GET) und der GET auf xmlrpc.php (405) wurden korrekt nicht gezählt.
Verifizieren: fail2ban-regex zeigt bei „Failregex“ genau so viele Treffer, wie Sie Fehlversuche ausgelöst haben, und die erfolgreiche Anmeldung erscheint unter „missed“.
Schritt 4: Jail anlegen und Grenzwerte festlegen
Die Jail verbindet Filter, Logdatei und Sperraktion. Legen Sie sie als eigene Datei an und ersetzen Sie 203.0.113.25 durch die feste IP-Adresse Ihres Büros, falls vorhanden:
nano /etc/fail2ban/jail.d/wordpress.local
[wordpress-login]
enabled = true
filter = wordpress-login
port = http,https
logpath = /var/log/apache2/access.log
maxretry = 5
findtime = 10m
bantime = 1h
ignoreip = 127.0.0.1/8 ::1 203.0.113.25
Die Werte bedeuten: Wer innerhalb von 10 Minuten (findtime) fünf Treffer (maxretry) erzeugt, wird für eine Stunde (bantime) auf den Ports 80 und 443 gesperrt. Das entspricht den Standardwerten von jail.conf bis auf die längere Sperrzeit (Standard 10 Minuten). Fünf Versuche lassen Mitarbeitern mit Tippfehler genug Spielraum. Setzen Sie die Sperrzeit nicht auf Tage: Viele Anschlüsse in Deutschland haben wechselnde IP-Adressen, eine lange Sperre trifft dann später einen Unbeteiligten.
Loggt jede Website in eine eigene Datei, geben Sie bei logpath alle Dateien an, eine je Zeile, oder ein Muster wie /var/log/apache2/*access.log. Unter nginx lautet der Standardpfad /var/log/nginx/access.log. Bei Systemen ohne klassische Logdateien, die nur ins Journal schreiben, gilt der Hinweis aus Linux-Server absichern mit UFW und Fail2ban.
Testen Sie die Konfiguration und laden Sie Fail2ban neu:
fail2ban-client -t
systemctl restart fail2ban
fail2ban-client status
Im Test lautete die Ausgabe von fail2ban-client -t „OK: configuration test is successful“, danach listete fail2ban-client status die Jail wordpress-login.
Verifizieren: fail2ban-client status wordpress-login zeigt unter „File list“ Ihre Logdatei und unter „Currently banned“ den Wert 0.
Schritt 5: Sperre testen, prüfen und aufheben
Testen Sie die Sperre nicht von Ihrem eigenen Anschluss, der steht in ignoreip. Nutzen Sie ein Smartphone im Mobilnetz und geben Sie sechsmal ein falsches Passwort ein. Danach lädt die Website auf diesem Gerät nicht mehr. Auf dem Server sehen Sie:
fail2ban-client status wordpress-login
Status for the jail: wordpress-login
|- Filter
| |- Currently failed: 1
| |- Total failed: 6
| `- File list: /var/log/apache2/access.log
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 203.0.113.10
Ruft ein Kunde an, weil er gesperrt ist, geben Sie die Adresse gezielt frei. Umgekehrt können Sie eine auffällige Adresse von Hand sperren:
fail2ban-client set wordpress-login unbanip 203.0.113.10
fail2ban-client set wordpress-login banip 198.51.100.7
fail2ban-client banned
Die Sperren selbst stehen bei Debian in der nftables-Tabelle f2b-table, anzeigen lassen sie sich mit nft list table inet f2b-table. Die Protokolldatei /var/log/fail2ban.log zeigt jede Sperre mit Zeitpunkt. Ein wöchentlicher Blick hinein gehört zur laufenden Pflege, genau wie Updates für WordPress, Plugins und Betriebssystem. Fail2ban bremst Angriffe nur, eine Sicherheitslücke in einem veralteten Plugin schließt es nicht. Wer diese Routine nicht selbst im Kalender halten möchte, kann sie abgeben: Die WordPress-Wartung von wordpressupdate spielt Updates von WordPress, Plugins und Themes nach Kompatibilitätsprüfung ein, sichert die Website wöchentlich auf externen Speicher und richtet die Firewall ein und pflegt sie.
Verifizieren: Nach sechs Fehlversuchen steht die Test-IP unter „Banned IP list“, und /var/log/fail2ban.log enthält eine Zeile mit Ban ohne Fehlermeldungen. Nach unbanip ist die Website vom Testgerät wieder erreichbar.
Typische Fehler
- „Have not found any log file for wordpress-login jail“: Der Pfad in
logpathstimmt nicht, im Test genügte ein Tippfehler im Dateinamen. Fail2ban startet die Jail dann nicht. Pfad mitlsprüfen und neu laden. - „ERROR: test configuration failed“: Eine Datei unter
jail.denthält ungültige Syntax, etwa Text ohne Abschnitt in eckigen Klammern.fail2ban-client -tnennt Datei und Zeile. - Treffer, aber keine Sperre, im Log „Operation not permitted (you must be root)“: Fail2ban erkennt die Angreifer, darf aber keine Firewall-Regel setzen. Im Test trat das in einem Container ohne Netzwerkrechte auf. Fail2ban muss als root direkt auf dem Server laufen, nicht in einem unprivilegierten Container.
- „Ignore 127.0.0.1 by ignoreself rule“: Alle Logzeilen tragen die Adresse des Servers selbst oder eines Proxys. Fail2ban ignoriert eigene Adressen, gesperrt wird nie jemand. Lösung siehe Schritt 2.
- 0 Treffer bei
fail2ban-regex: Das Logformat weicht vom Format „combined“ ab, oft steht der Hostname vor der IP-Adresse (Format „vhost_combined“). Dann passt das^<HOST>am Zeilenanfang nicht, der Ausdruck muss an das Format angepasst werden. Prüfen Sie zuerst eine echte Zeile des Logs. - Eigenes Büro gesperrt: Die feste IP-Adresse fehlt in
ignoreip, oder mehrere Mitarbeiter nutzen dieselbe Adresse und einer vertippt sich wiederholt. Adresse freigeben und inignoreipergänzen.
Häufige Fragen
Ersetzt Fail2ban ein Plugin zur Begrenzung der Anmeldeversuche?
Es ergänzt es. Fail2ban arbeitet vor WordPress und entlastet den Server, sieht aber nur IP-Adressen und Statuscodes. Ein Plugin sieht Benutzernamen und kann etwa Konten sperren. Wie Sie Plugin, starke Passwörter und zweiten Faktor kombinieren, beschreibt die Anleitung Brute-Force-Angriffe auf wp-login und XML-RPC abwehren.
Funktioniert das auf Shared Hosting?
Nein. Fail2ban braucht Root-Rechte für Pakete und Firewall. Auf Shared Hosting übernimmt der Hoster solchen Schutz, fragen Sie dort nach einer Begrenzung für wp-login.php.
Was ist mit verteilten Angriffen aus vielen Adressen?
Gegen ein Botnetz, das von jeder Adresse nur ein oder zwei Versuche schickt, wirkt Fail2ban kaum. Dagegen helfen starke Passwörter, ein zweiter Faktor und ein Passwortschutz vor wp-login.php.
Werden IPv6-Adressen gesperrt?
Ja, Fail2ban 1.1.0 verarbeitet IPv6-Adressen.
Testumfang
Wir haben die Anleitung in einer Laborinstanz mit WordPress 7.1.2, Apache und Fail2ban aus Debian 13 durchgespielt. Der Filter ließ sich mit fail2ban-regex prüfen, und nach fünf Fehlanmeldungen griff die Sperre wie vorgesehen.
Ob die nftables-Regel tatsächlich gesetzt wird, konnten wir nicht prüfen, weil der Labor-Container das nicht erlaubt. Auch systemd, nginx und Proxy-Konfigurationen blieben ungetestet, probieren Sie das deshalb zuerst auf einer Kopie aus.
Fazit
Mit einem eigenen Filter, der den Statuscode 200 auf POST-Anfragen an wp-login.php und xmlrpc.php auswertet, sperrt Fail2ban wiederholte Anmeldeversuche, bevor sie PHP und die Datenbank belasten. Entscheidend sind die richtige Logdatei, die echte Besucher-IP im Log und ein Eintrag für das eigene Büro in ignoreip. Fail2ban ist eine Schutzschicht unter mehreren, starke Passwörter und aktuelle Plugins bleiben Pflicht. Wenn Updates, Backups und Firewall dauerhaft in festen Händen liegen sollen, übernimmt das die WordPress-Wartung von Marcel Schönfelder.


