Nginx Rate Limiting: Schutz vor DDoS und Brute-Force
Auf meinem VPS sehe ich regelmäßig Phasen, in denen eine einzelne IP (oder ein kleines Botnet) versucht, über Login-Endpoints oder Admin-Pfade durchzukommen. Ohne Rate Limiting sind das tausende Requests pro Minute, die nicht nur Löcher suchen, sondern nebenbei die Performance für normale Nutzer tanken. Nginx bringt Rate Limiting als Built-in mit, und das ist eine der Features, die ich auf fast jedem vhost aktiviert habe.
Rate Limiting in nginx ist konzeptuell einfach — in der Praxis gibt es ein paar Konfigurations-Details, an denen man leicht vorbeirutscht.
Die zwei Zonen
Nginx unterscheidet zwischen Request-Rate und Connection-Rate. Beides brauchst du, je nachdem, was du schützt.
Request Rate (limit_req) begrenzt, wie viele Requests pro Zeiteinheit von einer IP kommen dürfen.
Connection Rate (limit_conn) begrenzt, wie viele gleichzeitige Verbindungen eine IP haben darf.
Für typische Web-Apps ist Request Rate das Relevante. Connection Rate ist sinnvoll für WebSockets oder Download-Endpoints.
Zone definieren
Im http-Block (nicht im server-Block!):
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
$binary_remote_addr— Key für die Zone. Hier die IP.zone=api:10m— Name und Größe des Shared-Memory-Blocks. 10m reicht für ca. 160.000 IPs.rate=30r/m— 30 Requests pro Minute erlaubt
Zwei separate Zonen: eine mit 30 r/m für API-Endpoints, eine mit 5 r/m für Login. Login hat eine strengere Grenze, weil Brute-Force genau da zuschlägt.
Zone anwenden
Im server- oder location-Block:
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend;
}
location /login {
limit_req zone=login burst=3 nodelay;
proxy_pass http://backend;
}
burst ist der entscheidende Parameter. Die Rate sagt "30 Requests pro Minute" — aber reale Clients machen oft 5 Requests in einer Sekunde, dann eine Minute nichts. Ohne burst wird jeder Request ab dem 2. in der Sekunde abgelehnt.
burst=20 erlaubt Bursts von bis zu 20 Requests, die dann in der folgenden Zeit "abgebaut" werden.
nodelay bedeutet: die Burst-Requests werden sofort verarbeitet, nicht verzögert. Ohne nodelay würde nginx die Requests rate-limiten statt sie abzulehnen — das Verhalten ist für APIs meistens nicht gewollt.
Was bei einer Überschreitung passiert
Nginx gibt einen 503 Service Unavailable zurück. Das ist technisch nicht ganz richtig — 429 Too Many Requests wäre semantisch besser — und kann per Config geändert werden:
limit_req_status 429;
Damit verhalten sich Clients, die den 429er kennen (die meisten SDKs), automatisch richtig mit Retry-After.
Whitelist für bekannte IPs
Manchmal willst du bestimmte IPs (interne Tools, Monitoring) vom Rate Limiting ausnehmen:
geo $limit_req_key {
default $binary_remote_addr;
10.0.0.0/8 "";
192.168.1.100 "";
}
limit_req_zone $limit_req_key zone=api:10m rate=30r/m;
Ein leerer String als Key deaktiviert das Limiting für diese IP. Das ist eleganter als bedingte Regeln in den Locations.
Hinter Cloudflare oder Load-Balancer
Wenn nginx hinter einem Reverse Proxy sitzt, ist $binary_remote_addr immer die IP des Proxies — alle Clients werden in einen Topf geworfen und blockieren sich gegenseitig.
Lösung: die echte Client-IP auslesen. Bei Cloudflare:
set_real_ip_from 173.245.48.0/20;
real_ip_header CF-Connecting-IP;
Die vollständige Cloudflare-Range-Liste gibt's auf cloudflare.com/ips/. Die müssen regelmäßig aktualisiert werden.
Logging, wenn Limits greifen
Standardmäßig schreibt nginx Rate-Limit-Events in das error.log:
limiting requests, excess: 20.000 by zone "login", client: 1.2.3.4
Das reicht für manuelle Analyse, aber für strukturiertes Monitoring will man mehr. Mein Workaround: ein separates Log mit eigenem Format für Rate-Limit-Events, das dann von Prometheus per Log-Exporter gescraped wird. Oder gleich ein fail2ban-Jail, der IPs dauerhaft sperrt, die regelmäßig Limits reißen.
Was Rate Limiting nicht löst
Rate Limiting schützt gegen einzelne aggressive IPs und unsophisticated Bots. Gegen verteilte Angriffe (Distributed DDoS, echte Botnets mit Tausenden IPs) hilft es kaum — jede IP macht dann "legitime" wenige Requests, und in der Summe ist der Server trotzdem überlastet.
Für größere Angriffe brauchst du ein echtes WAF (Cloudflare, AWS Shield) oder ein spezielles DDoS-Protection-Setup. Rate Limiting ist die erste, nicht die einzige Verteidigung.
Was ich für meine Setups nutze
Standard-Rate für alle API-Endpoints: 60 r/m mit burst 30. Login-Endpoints: 10 r/m mit burst 3. Bei statischen Assets kein Rate Limiting, weil Browser-Bursts da legitim sind. Plus Cloudflare davor für alle Domains, die mehr als "hobby" sind.