Warum Rate Limiting?
Jede API hat eine begrenzte Kapazität, und nicht jeder Aufrufer verhält sich vorbildlich. Ein Scraper, ein fehlerhafter Client, der in einer Retry-Schleife feststeckt, oder ein plötzlicher Anstieg legitimen Traffics können Ihre Datenbankverbindungen erschöpfen und den Dienst für alle Nutzer lahmlegen. Rate Limiting begrenzt die Geschwindigkeit, mit der ein Client Anfragen stellen darf, sodass kein einzelner Aufrufer das gesamte System beanspruchen kann.
Zudem macht es die Kosten planbar, stellt die faire Nutzung über verschiedene Tenants und Pläne hinweg sicher und gibt Ihnen einen Hebel an die Hand, um Missbrauch zu stoppen, bevor er zu einem Systemausfall führt.
Was begrenzt werden sollte
Ein Limit ist nur im Verhältnis zu einer Identität sinnvoll. Gängige Optionen sind:
- API key — die beste Wahl für Server-to-Server-Kommunikation und Drittanbieter-Clients.
- User id — natürlich für authentifizierte Apps.
- IP address — ein Fallback für anonymen Traffic, wobei viele Nutzer dieselbe IP teilen können.
- Kombination — Key plus IP oder User plus Endpoint für mehr Präzision.
- Endpoint oder Tier — strengere Limits für rechenintensive Operationen wie Suche oder Exporte.
Entscheiden Sie zudem über den Scope: ein globales Limit, ein Limit pro Endpoint oder beides. Ein globales Limit schützt den gesamten Service; Limits pro Endpoint schützen spezifische, ressourcenintensive Prozesse.
Algorithmen
Vier Algorithmen decken fast jeden Bedarf ab.
Fixed window — zählt Anfragen in einem festen Intervall und setzt den Zähler an der Grenze zurück.
limit: 100 per minute
key: client:123:2026-09-15T10:05
Dieser Ansatz ist kostengünstig und einfach, aber ein Client kann 100 Anfragen am Ende eines Fensters und 100 zu Beginn des nächsten senden, wodurch die Rate an der Grenze effektiv verdoppelt wird.
Sliding window — zählt über die letzten N Sekunden statt über einen festen Block, meist durch die Kombination des aktuellen und des vorherigen Fensters mittels eines gewichteten Durchschnitts. Dies ist flüssiger als das Fixed window bei ähnlichen Kosten.
Token bucket — ein Bucket füllt sich mit einer konstanten Rate, und jede Anfrage verbraucht einen Token. Dies erlaubt kurze Bursts bis zur Größe des Buckets, während eine durchschnittliche Rate erzwungen wird, was dem Verhalten echter Clients entspricht.
// token-bucket.js
const capacity = 20;
const refillPerSecond = 5;
let tokens = capacity;
let last = Date.now();
function allow() {
const now = Date.now();
tokens = Math.min(capacity, tokens + ((now - last) / 1000) * refillPerSecond);
last = now;
if (tokens < 1) return false;
tokens -= 1;
return true;
}
Leaky bucket — Anfragen gelangen in eine Queue, die mit einer festen Rate geleert wird. Dies glättet den Traffic auf einen konstanten Output, was nützlich ist, wenn nachgelagerte Systeme eine gleichmäßige Last benötigen.
Reaktion auf Limits
Informieren Sie Clients über standardisierte Signale darüber, was gerade passiert.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 30
- 429 Too Many Requests ist der korrekte Statuscode.
- Retry-After teilt dem Client mit, wann ein erneuter Versuch erfolgen soll (in Sekunden oder als Datum).
- RateLimit-* Header legen das Limit, die verbleibende Anzahl und die Reset-Zeit offen, damit Clients ihr Tempo anpassen können.
Die Rückgabe von 200 oder das lautlose Verwerfen von Anfragen verschleiert das Problem und führt zu mysteriösen Bugs auf der Client-Seite. Gut implementierte Clients lesen Retry-After und reduzieren ihre Anfragen automatisch.
Verteilte Limitierung
Wenn Sie mehr als eine Instanz betreiben, müssen die Zähler gemeinsam genutzt werden. In-Memory-Limits gelten pro Instanz; bei zehn Servern erhält ein Client also effektiv das Zehnfache des Limits.
// redis.js
const key = `ratelimit:${apiKey}`;
const count = await redis.incr(key);
if (count === 1) await redis.expire(key, 60);
if (count > 100) {
// reject with 429
}
Verwenden Sie atomare Operationen oder eine Library, die Lua-Skripte nutzt, damit Inkremente und Ablaufzeiten (Expiries) race-free sind. Redis ist hierfür der übliche Store, da es schnell ist und sowohl atomare Skripte als auch Expiry unterstützt. API-Gateways und Edge-Plattformen können Limits bereits erzwingen, bevor der Traffic überhaupt Ihre Anwendung erreicht, was der effizienteste Ort dafür ist.
Quotas versus Rate Limits
Sie lösen unterschiedliche Probleme und existieren oft nebeneinander.
- Rate Limit — wie schnell: 100 Requests pro Minute.
- Quota — wie viel: 100.000 Requests pro Monat, gekoppelt an einen Plan.
Ein Client kann den gesamten Monat über unter seinem Rate Limit bleiben und trotzdem seine Quota aufbrauchen. Tracken Sie Quotas separat, üblicherweise mit einem länger laufenden Counter, und geben Sie einen eindeutigen Fehler zurück, wenn eine Quota erschöpft ist, damit Clients den Unterschied erkennen können.
Vermeidung von Beeinträchtigungen für echte Nutzer
Limits sollten Missbrauch verhindern, ohne die normale Nutzung zu bestrafen.
- Setzen Sie Limits basierend auf gemessenem Traffic fest und planen Sie Puffer für Lastspitzen ein.
- Ermöglichen Sie Bursts mithilfe eines Token Bucket anstatt eines harten Limits.
- Verwenden Sie unterschiedliche Limits pro Tier und pro Endpoint.
- Schließen Sie Health Checks, interne Aufrufe und statische Assets aus.
- Bevorzugen Sie temporäre Drosselungen gegenüber permanenten Bans.
- Überwachen und optimieren Sie die Ablehnungsraten; ein Anstieg von 429s kann bedeuten, dass ein Limit zu niedrig angesetzt ist.
Best Practices
- Identifizieren Sie Clients mit dem spezifischsten verfügbaren stabilen Key.
- Wählen Sie einen Algorithmus, der zum Traffic-Profil passt.
- Geben Sie einen 429-Statuscode zusammen mit
Retry-Afterund Rate-Limit-Headern zurück. - Teilen Sie Counter in Redis oder erzwingen Sie Limits am Edge.
- Trennen Sie Rate Limits von Quotas und stellen Sie beides bereit.
- Erlauben Sie Bursts und legen Sie Limits basierend auf realen Messwerten fest.
- Loggen und überwachen Sie Ablehnungen und richten Sie Alerts für plötzliche Spitzen ein.
Häufige Fehler
- Zählen pro Instanz und Multiplizieren des effektiven Limits.
- Die Verwendung von ausschließlich IP-Adressen, was Nutzer hinter einem Shared NAT benachteiligt.
- Das Zurückgeben von 200 oder das lautlose Verwerfen von Anfragen.
- Das Setzen von so strikten Limits, dass normale Clients blockiert werden.
- Das Vergessen, Counter ablaufen zu lassen, was zu Memory Leaks führt.
- Die Behandlung von Rate Limits als Ersatz für Authentifizierung und Autorisierung.
Wie geht es weiter?
Rate Limiting sorgt dafür, dass eine API auch unter hoher Last verfügbar bleibt. Vertiefen Sie Ihr Wissen mit REST-Design und dem HTTP-Guide, kombinieren Sie es im Rahmen des Lebenszyklus mit API-Versioning und implementieren Sie es in Node.js. Fügen Sie anschließend einen Limiter zu einem Endpunkt hinzu und beobachten Sie, wie dieser sich unter einem Lasttest verhält.