Nginx Rate Limiting gegen Brute Force

Nginx Rate Limiting Dashboard

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üssel
  • zone=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 werden
  • nodelay – überschüssige Requests werden sofort verarbeitet, nicht verzögert
    • Ohne nodelay verzögert Nginx Requests mit zunehmender Verzögerung (ab rate=1r/s spürbar)

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

FehlerFolgeLösung
burst zu hoch gesetztAngreifer kann trotzdem viele Requests sendenburst niedrig halten (2–5)
Keine nodelayNutzer erlebt spürbare VerzögerungenImmer nodelay für Login-Endpoints
Eine Zone für alle PfadeAPI-Nutzer werden geblocktSeparate Zones pro Pfadgruppe
Zone zu klein (5m)Überschreibungen, ungenaue ZählungMindestens 10m pro Zone
Kein MonitoringMan merkt nicht, wenn Angriffe stattfindenLogs 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/)