Nginx Rate Limiting gegen Brute Force

Letzten Monat sah ich in meinen Nginx-Logs über 50.000 fehlgeschlagene Login-Versuche pro Tag. WordPress- und PHP-Endpoints wurden mit Bruteforce-Angriffen überflutet. Ohne Gegenmaßnahmen blockierte das nicht nur legitime Nutzer – es belastete auch die Datenbank unnötig. Hier ist, wie man Nginx Rate Limiting richtig konfiguriert.
Problem: Ungestörte Brute-Force-Angriffe
Brute-Force-Angriffe sind einfach und effektiv: Ein Angreifer probiert tausende Passwort-Kombinationen pro Minute gegen Login-Endpoints wie /wp-login.php, /xmlrpc.php oder beliebige API-Routen. Ohne Rate Limiting erreichen diese Requests den Application-Server ungehindert. Die Folgen:
- Hohe CPU- und Datenbanklast durch permanente Passwort-Hashes
- Legitime Nutzer werden durch überlastete Server ausgebremst
- Keine Möglichkeit, einzelne Angreifer-IPs zu blockieren
- Log-Fluten machen die spätere Analyse schwieriger
Ursache: Nginx keine Anfragebegrenzung per Default
Nginx ist auf Durchsatz optimiert, nicht auf Angriffserkennung. Standardmäßig gibt es keine Begrenzung, wie oft eine einzelne IP innerhalb eines Zeitfensters Anfragen stellen darf. Das ngx_http_limit_req_module ist zwar mitgeliefert, muss aber explizit aktiviert werden. Viele Admins wissen nicht, dass sich damit nicht nur einzelne IPs, sondern auch spezifische Pfade oder User-Agents gezielt drosseln lassen.
Lösung: Zone, Geschwindigkeit und Request-Begrenzung
Das Modul arbeitet mit drei Komponenten: einer Zone (Shared Memory), einer Rate (Verarbeitungsgeschwindigkeit) und einem Limit-Key (z.B. IP-Adresse). Die Konfiguration erfolgt in http {}, server {} oder location {}.
1. Zone definieren
Zuerst legt man eine Shared-Memory-Zone an. Diese speichert den Zustand für jede IP:
# In /etc/nginx/nginx.conf, innerhalb von http {}
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;
Erklärung:
$binary_remote_addr– komprimierte IP als Schlüsselzone=login:10m– 10 Megabyte Shared Memory (~160.000 Sessions)rate=10r/m– maximal 10 Requests pro Minute pro IP
2. Location-Block schützen
Dann wendet man die Zone auf konkrete Pfade an:
server {
listen 80;
server_name example.com;
location = /wp-login.php {
limit_req zone=login burst=5 nodelay;
# Ursprünglichen Request proxypassen oder PHP-FPM
include fastcgi_params;
fastcgi_pass unix:/run/php/php-fpm.sock;
}
location = /xmlrpc.php {
limit_req zone=login burst=2 nodelay;
# XML-RPC fast komplett sperren, falls nicht benötigt
return 403;
}
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://127.0.0.1:8080;
}
}
Die Parameter im Detail:
burst=5– erlaubt bis zu 5 überschüssige Requests, die in die Warteschlange gestellt werdennodelay– überschüssige Requests werden sofort verarbeitet, nicht verzögert- Ohne
nodelayverzögert Nginx Requests mit zunehmender Verzögerung (abrate=1r/sspürbar)
- Ohne
3. Für differenzierte Pfade eigene Zones
Für API-Routen mit anderen Anforderungen lohnt sich eine zweite Zone mit höherem Limit:
# Zusätzliche Zone für API-Endpoints
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m;
Praxistest: Verifizieren und Monitoren
1. Konfiguration testen
nginx -t
systemctl reload nginx
2. Angriffe simulieren
# 100 Requests in 2 Sekunden von der gleichen IP
for i in {1..100}; do curl -s -o /dev/null -w "%{http_code}
" http://example.com/wp-login.php; done
Erwartetes Ergebnis: Die ersten Requests liefern 200, dann folgen 503 (Service Unavailable).
3. Logs auswerten
# Rate-Limit-Treffer in den Logs suchen
grep "limiting requests" /var/log/nginx/error.log
Häufige Fehler und wie man sie vermeidet
| Fehler | Folge | Lösung |
|---|---|---|
burst zu hoch gesetzt | Angreifer kann trotzdem viele Requests senden | burst niedrig halten (2–5) |
Keine nodelay | Nutzer erlebt spürbare Verzögerungen | Immer nodelay für Login-Endpoints |
| Eine Zone für alle Pfade | API-Nutzer werden geblockt | Separate Zones pro Pfadgruppe |
Zone zu klein (5m) | Überschreibungen, ungenaue Zählung | Mindestens 10m pro Zone |
| Kein Monitoring | Man merkt nicht, wenn Angriffe stattfinden | Logs regelmäßig auswerten |
Fazit
Nginx Rate Limiting ist kein optionales Feature – es ist eine Pflichtmaßnahme für jeden öffentlich erreichbaren Server. Die Konfiguration ist einfach, die Auswirkungen direkt spürbar: Weniger Last, weniger Angriffsfläche, zufriedenere Nutzer.
Der Schlüssel liegt in der differenzierten Konfiguration: Nicht alle Pfade brauchen das gleiche Limit. Login-Endpoints sind kritisch, API-Routen dürfen etwas mehr vertragen. Mit der richtigen Zone-Größe, einem realistischen rate-Wert und burst=2–5 hat man die meisten Angriffe im Griff.
Und das Wichtigste: Testen, bevor etwas schiefgeht. Simuliere den Angriff in einer Staging-Umgebung, prüfe die Logs und justiere die Parameter. Eine falsch konfigurierte Rate-Limiting-Regel kann mehr Schaden anrichten als gar keine – nämlich dann, wenn legitime Nutzer plötzlich ausgesperrt werden.
Fragen zur Nginx-Konfiguration? Ich helfe gern – schreib mir auf Telegram oder hinterlasse einen Kommentar.
Siehe auch
[Wenn die CI/CD-Pipeline streikt – ein praxisnaher Leitfaden zur Reparatur](/blog/ci-cd-pipeline-reparatur-leitfaden/)[Nginx oder Caddy? Eine Architekturentscheidung](/blog/nginx-oder-caddy-eine-architekturentscheidung/)