Rate Limiting in Bun.js: In-Memory, Redis, Sliding Window und Edge Cases für Production APIs
Ein praktischer Deep Dive zu Rate Limiting Middleware in Bun.js: Fixed Window, Sliding Window, Token Bucket, Redis, Distributed Limits, 429 Retry-After, Abuse Protection, Hono/Elysia Integrationen, Best Practices und schlechte Praktiken.

Bun.js Middleware Production Guide 2026
Eine Serie über Production Middleware in Bun.js: Überblick, Security, Performance, Observability, Rate Limiting, Body Parsing, WebSocket/SSE und Tests für die Request Pipeline.
Alle Artikel in diesem Guide
01
Bun.js Middleware 2026: Overview, Best Practices und Anti-Patterns
Das grundlegende mentale Modell für Middleware in nativem Bun, Hono und Elysia, mit Beispielen, Optimierung und Roadmap für die nächsten Deep-Dive-Artikel.
02
Auth Middleware in Bun.js: JWT, Sessions, API Keys und Multi-Tenant Context
Wie man Auth Middleware in Bun richtig baut: Reihenfolge der Prüfungen, Cache, Token Rotation, Tenant Context, 401/403-Fehler und Tests.
03
Rate Limiting in Bun.js: in-memory, Redis, Sliding Window und Edge Cases
Ein detaillierter Blick auf Rate Limiting für Bun APIs: Algorithmen, Redis, distributed limits, abuse protection und graceful degradation.
04
Observability Middleware in Bun.js: Logs, Request ID, Tracing und Latency Budgets
Wie man request id, structured logs, timing headers, einen OpenTelemetry-ähnlichen Flow hinzufügt und Logging nicht zum Bottleneck macht.
05
Body Parsing und Validation in Bun.js: JSON, Uploads, Streams und Payload Limits
Wie man Request Bodies in Bun sicher liest, wo Limits hingehören und wie man Streams, Uploads, Idempotency und Schema Validation nicht kaputtmacht.
Ein naives Limit wie „100 Requests pro IP pro Minute“ sieht genau bis zum ersten Büro hinter NAT, dem ersten Mobilfunkanbieter, dem ersten Webhook Retry Storm oder dem ersten Enterprise Tenant mit hunderten Nutzern hinter einer Egress IP gut aus.
Rate Limiting muss nicht nur „wie viele Requests?“ beantworten, sondern auch: „wer wird limitiert?“, „welche Route?“, „welcher Tenant?“, „welches Credential?“, „was passiert, wenn Redis nicht verfügbar ist?“, „gibt es Retry-After?“ und „brechen wir einen normalen Burst?“.
Bun macht den HTTP Layer schnell, aber Rate Limiting hängt immer an Key Design, Storage Atomicity und Failure Policy. Genau das zerlegen wir hier.
429 ohne Retry-After macht guten Clients das Leben schwerer.karte
Das ist das dritte Chapter der Bun Middleware Serie. Nach Overview und Auth ist Abuse Protection der logische nächste Schritt: Rate Limiting steht oft zwischen deiner API und einer teuren Welle unnötiger Requests.
Ein Rate Limiter sollte eine kurze State Machine sein, nicht eine zufällige Prüfung mitten im Handler. Wenn man ihn in Schritte aufteilt, werden die meisten Fehler vor dem Code sichtbar.
Identity Key bestimmen
Das kann user id, API key id, tenant id, route group, IP oder eine Kombination sein. Für einen authenticated API key ist apiKeyId + routeGroup meistens besser als nur IP.
Policy wählen
Unterschiedliche Routes brauchen unterschiedliche Limits: login, search, export, webhook, public read, admin mutation. Ein globales Limit ist fast immer entweder zu schwach oder zu aggressiv.
Counter atomar aktualisieren
In einem Prozess ist das eine Map. In einer Distributed API ist es Redis oder ein anderer Shared Store. Für Redis müssen increment + expiry oder Sliding-Window-Update atomar sein.
Nützliches 429 zurückgeben
Der Client sollte einen stabilen JSON Error, Retry-After und idealerweise Rate-Limit-Headers bekommen. Sonst wissen gute Clients nicht, wann sie wiederholen sollen.
Ohne Secrets loggen
Logge limiter key hash/prefix, route group, tenant, decision, remaining und reset time. Logge keinen vollständigen API Key oder Authorization Header.
Kurz gesagt
Wenn du keine explizite Antwort auf jeden dieser Schritte hast, ist der Limiter noch nicht production-ready.
Der Algorithmus bestimmt nicht nur Genauigkeit, sondern auch UX. Zwei Clients können gleich viele Requests pro Minute haben, aber einer erzeugt einen Burst an der Fenstergrenze, der andere verteilt gleichmäßig.
| Algorithmus | Wie er funktioniert | Wann geeignet | Schwachstelle |
|---|---|---|---|
| Fixed window | Counter für ein fixes Fenster, zum Beispiel 100 Requests pro Minute | Einfache internal endpoints, low-risk APIs, günstiger Baseline | Boundary Burst: Client kann viele Requests an der Grenze zweier Fenster senden |
| Sliding window log | Speichert Timestamps von Requests und zählt nur solche im beweglichen Fenster | Kritische APIs, Login, Checkout, teure Operations | Mehr Storage und Cleanup Work pro Request |
| Sliding window counter | Approximiert ein bewegliches Fenster über aktuelles und vorheriges Bucket-Fenster | Balance aus Genauigkeit und Kosten für High-Traffic APIs | Weniger genau als Log-Variante, braucht vorsichtige Reset-Mathematik |
| Token bucket | Client hat einen Bucket aus Tokens, der über Zeit aufgefüllt wird | APIs, bei denen ein kurzer Burst normal ist, aber die durchschnittliche Rate kontrolliert sein muss | Capacity und Refill Rate müssen richtig gewählt werden |
Fixed Window ist simpel, Sliding Window genauer, Token Bucket toleriert legitime Bursts besser.
Screenshot des Abschnitts algorithm-choiceKurz gesagt
Starte mit Fixed Window für einen einfachen Baseline, aber für teure öffentliche Routes sind Sliding Window oder Token Bucket meist besser.
Für einen Bun Prozess kann man einen einfachen In-Memory Fixed-Window Limiter schreiben. Er ist nützlich für local dev, internal tools, single-instance deployments oder als fallback, synchronisiert sich aber nicht zwischen Instanzen.
Minimaler Beispielcode:
type LimitEntry = { count: number; resetAt: number };
const limits = new Map<string, LimitEntry>();
const WINDOW_MS = 60_000;
const MAX_REQUESTS = 120;
function rateLimitKey(req: Request) {
const apiKeyId = req.headers.get("x-api-key-id");
const ip = req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? "unknown";
return apiKeyId ? `api:${apiKeyId}` : `ip:${ip}`;
}
function checkLimit(key: string, now = Date.now()) {
const current = limits.get(key);
if (!current || current.resetAt <= now) {
limits.set(key, { count: 1, resetAt: now + WINDOW_MS });
return { allowed: true, remaining: MAX_REQUESTS - 1, resetAt: now + WINDOW_MS };
}
if (current.count >= MAX_REQUESTS) {
return { allowed: false, remaining: 0, resetAt: current.resetAt };
}
current.count += 1;
return { allowed: true, remaining: MAX_REQUESTS - current.count, resetAt: current.resetAt };
}
Bun.serve({
async fetch(req) {
const decision = checkLimit(rateLimitKey(req));
if (!decision.allowed) {
const retryAfter = Math.ceil((decision.resetAt - Date.now()) / 1000);
return Response.json(
{ error: "rate_limited", retryAfter },
{ status: 429, headers: { "Retry-After": String(retryAfter) } },
);
}
return Response.json({ ok: true, remaining: decision.remaining });
},
});Das ist ein Fixed-Window Baseline. Er räumt alte Keys nicht aktiv auf, funktioniert nicht über mehrere Prozesse hinweg, hat keine tenant-specific policies und schützt nicht vor Boundary Bursts. Aber er zeigt die richtige Form: key, counter, decision, 429, Retry-After.
Wo das sinnvoll ist
Ein In-Memory Limiter passt für einen Prozess oder als günstiger lokaler Guard. Eine Production API mit Autoscaling braucht einen Shared Store.
Sobald eine Bun API in mehreren Instanzen läuft, sind In-Memory Counters kein globales Limit mehr. Ein Client kann Requests über Instanzen verteilen und bekommt einen Multiplikator auf sein Limit. Redis löst das als Shared Counter Store.
Das kritische Detail: Counter Update muss atomar sein. Für Fixed Window ist das oft INCR + EXPIRE, aber expiry muss beim ersten Increment korrekt gesetzt werden. Für Sliding Window Log nutzt man häufig Sorted Sets und Cleanup alter Timestamps. Für Token Bucket braucht man oft ein Lua Script oder einen anderen atomaren Mechanismus, der refill und consume in einer Operation berechnet. Redis Rate Limiting Patterns basieren typischerweise auf atomaren Counters oder Lua. [5][6]
Redis fügt außerdem einen Failure Mode hinzu: Was passiert, wenn Redis nicht verfügbar ist? Für teure public routes ist fail closed oder degraded limit oft sicherer. Für eine kritische interne Control Plane kann eine andere Policy gelten. Aber jedes fail-open braucht Alerts, weil der Rate Limiter sonst genau dann verschwindet, wenn er am meisten gebraucht wird.
Für mehrere Bun Instanzen muss das Limit in einem Shared Store leben. Sonst gibt jede Instanz dem Client eine eigene Allowance.
Screenshot des Abschnitts redis-distributedPraktischer Baseline
Ein Distributed Limiter braucht Shared Store, atomaren Update, Key Design, TTL Cleanup, Latency Budget und Fail Policy.
Fertige Middleware oder ein Plugin ist nützlich, wenn die Aufgabe typisch ist. Aber Key Strategy, Tenant Policy und Distributed Storage löst es nicht für dich.
Das Hono Ecosystem hat Rate Limiter Middleware mit konfigurierbarem Window, Limit, Key Generator und Store Options. Das ist gut für einen schnellen Baseline in einer Hono App. [1]
Das Elysia Ecosystem hat ein Rate-Limit Plugin für Bun-first Elysia Apps. Es passt natürlich in das Lifecycle-/Plugin-Modell von Elysia. [2]
OWASP API Security Top 10 führt unrestricted resource consumption als eigenes Risiko. Rate Limiting sollte CPU, Memory, Storage, Network und Downstream Resources schützen. [3]
Kurz gesagt
Ein fertiger Limiter reduziert Boilerplate. Production-Qualität bestimmen Keys, Storage, Policies, Observability und Edge Cases.
Der Rate Limit Key ist die wichtigste Entscheidung. Wenn der Key falsch ist, rettet der Algorithmus nicht mehr. IP-only Limits bestrafen oft normale Nutzer hinter NAT und erkennen authenticated abuse schlecht.
Für öffentliche anonyme Routes kann IP ein Startpunkt sein. Für authenticated APIs ist es besser, nach subject id, API key id, tenant id oder einer Kombination wie tenantId + routeGroup zu limitieren. Für Login Flows braucht man manchmal gleichzeitig IP Limit, Account/Email Limit und Device Fingerprint Policy.
Für Multi-Tenant SaaS kann ein reines User-Limit zu weich sein, weil ein Tenant mit vielen Usern eine Ressource überlasten kann. Ein reines Tenant-Limit kann zu hart sein, weil ein noisy user die ganze Firma blockiert. Oft braucht man eine Hierarchie: per-user, per-tenant, per-route und global emergency limit.
HTTP 429 Too Many Requests bedeutet, dass der Nutzer in einem bestimmten Zeitraum zu viele Requests gesendet hat. MDN beschreibt, dass die Response Retry-After enthalten kann, um mitzuteilen, wie lange der Client vor einem Retry warten soll. [7]
In einer Production API zwingt 429 ohne Retry-After gute Clients zum Raten. Sie wiederholen entweder zu schnell oder nutzen Exponential Backoff, obwohl sie einfach bis zum Reset warten könnten. Das verschlechtert UX und erhöht unnötige Last.
Neben Retry-After fügen viele APIs Rate-Limit-Headers hinzu, zum Beispiel RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset oder eigene X-RateLimit-*. Wichtig sind stabiler Contract und Dokumentation für Clients.
429 sollte nützlich sein: Der Client sollte wissen, wann er retryen kann, statt Backoff zu raten.
Kurz gesagt
Ein Rate Limiter sollte für den Client verständlich sein. 429 ohne Retry Contract erzeugt wiederholten Traffic und schlechte Integration.
Rate Limiting hat viele unangenehme Details, die im Happy Path unsichtbar sind. Unten sind die, die nach Launch am häufigsten auftauchen.
NAT und Corporate Networks
Ein IP-only Limit kann dutzende normale Nutzer hinter einer Egress IP blockieren. Für authenticated APIs besser nach subject/API key/tenant keyen.
Webhook Retry Storms
Ein Partner kann failed Webhooks ehrlich retryen und in den Limiter laufen. Webhooks brauchen eigene Policies, Idempotency und retry-aware Contract.
Clock Skew
Wenn der Limiter über verschiedene Systeme verteilt ist, müssen reset time und sliding windows stabil berechnet werden. Redis Server Time oder ein zentraler Store sind oft zuverlässiger als lokale Clocks.
Burst nach Deploy
Nach Downtime oder Deploy können Clients synchron Requests wiederholen. Token Bucket oder queued backoff kann besser sein als ein hartes Fixed Window.
Admin und Internal Routes
Gib internen Tools nicht standardmäßig unbegrenzten Zugriff. Sie starten oft die schwersten Exports und Batch Operations.
Redis Failure
Fail-open ohne Alerts macht Schutz unsichtbar. Fail-closed ohne Degradation kann das Produkt lahmlegen. Policy sollte pro Route Class unterschiedlich sein.
Diese Fehler sind nicht einzigartig für Bun, aber in Bun APIs verstecken sie sich oft hinter schnellem Runtime und einfachem Middleware Wrapper.
Ein globales Limit für alle Routes.
IP-only Limit für authenticated APIs.
In-Memory Limiter in Multi-Instance Production.
Redis INCR ohne korrektes TTL oder Atomicity.
Kein Retry-After in der 429 Response.
Limits berücksichtigen Tenant, API Key oder Route Cost nicht.
Vollständiger API Key oder Authorization Header landet als Limiter Key in Logs.
Fail-open bei Redis Outage ohne Alerting.
Rate Limiter steht nach Body Parsing für teure Payload Routes.
Keine Tests für Boundary Burst, Reset, Redis Failure und Concurrent Requests.
Review-Regel
Der Rate Limiter sollte früh stehen, den richtigen Key haben, einen atomaren Counter, klares 429 und observable decision.
Vor dem Start von Rate Limiting in Staging oder Production sollte diese Liste geprüft werden. Sie hilft, Probleme zu finden, bevor Kunden sie finden.
Route Classes sind definiert
Public, auth, login, webhook, export, admin und internal routes haben unterschiedliche Policies.
Key Strategy ist nicht IP-only
Für authenticated routes subject/API key/tenant/route group nutzen und IP als zusätzliches Signal behalten.
Distributed Store existiert für Multi-Instance
Wenn die Bun API mehrere Instanzen hat, leben Counters in Redis oder einem anderen Shared Store.
Operationen sind atomar
Increment, expiry, sliding window cleanup oder token consume laufen ohne Race Conditions.
429 hat Retry Contract
Response enthält stabilen JSON Error und Retry-After; Rate-Limit-Headers sind dokumentiert.
Limiter steht vor teuren Operationen
Rate Check passiert vor Body Parsing, DB Calls, Remote Calls und schweren Transforms, wenn die Route es erlaubt.
Redis Failure Policy ist definiert
Für jede Route Class ist bekannt, ob fail-open oder fail-closed gilt, und Alerting existiert.
Observability existiert
Decision, route group, limiter key hash, remaining, reset time, storage latency und Redis failures werden geloggt.
Bun gibt dir einen schnellen HTTP Runtime, aber Rate Limiting ist kein Runtime Feature, das man mit einer Zeile hinzufügt und vergisst. Es ist eine Security- und Reliability-Policy, die wissen muss, wen sie limitiert, für welche Route, mit welchem Storage, welchem Algorithmus und welchem Retry Contract.
Für einen Prozess kann ein In-Memory Fixed Window ein guter Baseline sein. Für Production mit mehreren Instanzen braucht man Redis oder einen anderen Shared Store. Für user-facing APIs ist Fixed Window oft zu grob; Sliding Window oder Token Bucket geben bessere UX.
Am wichtigsten: Bestrafe normale Nutzer nicht durch einen schlechten Key. IP-only Limit, ein globales Limit und 429 ohne Retry-After erzeugen meistens mehr Probleme, als sie lösen.
Nur für einen Prozess, local dev, internal tools oder einen einfachen Baseline. Wenn die API mehrere Instanzen hat, multipliziert sich ein In-Memory Limit mit der Anzahl der Instanzen und ist kein globaler Schutz.
Fixed Window ist am einfachsten, hat aber Boundary Bursts. Sliding Window ist genauer für kritische Routes, aber teurer. Token Bucket erlaubt kurze legitime Bursts und kontrolliert gleichzeitig die durchschnittliche Rate.
Ein IP-only Limit kann normale Nutzer hinter NAT, Corporate Proxy oder Mobilfunkanbieter blockieren und gleichzeitig schlecht gegen authenticated abuse funktionieren. Für APIs ist es besser, nach subject, API key, tenant und route group zu limitieren.
Einen stabilen JSON Error mit HTTP `429`, den Header `Retry-After` und idealerweise Rate-Limit-Headers wie remaining/reset. Das hilft guten Clients, korrekt zu retryen. [7]
Für einen Prozess nicht zwingend. Für Production mit mehreren Instanzen oder serverless/concurrent deployment ist Redis oder ein anderer Shared Store praktisch nötig, damit Counters gemeinsam sind.
Fail Policy vorher definieren. Für teure public routes ist fail closed oder ein degraded strict local limit oft sicherer. Für einzelne internal/control-plane routes kann fail-open möglich sein, aber nur mit Alerting und Audit.
Diese Quellen bestätigen fertige Middleware-/Plugin-Optionen, Security Rationale für Resource Limiting, Redis Rate Limiting Patterns und HTTP Semantics für 429.
PAS7 Studio kann helfen, Rate Limiting für Bun, Hono oder Elysia zu entwerfen: route classes, Redis store, sliding window oder token bucket, API-key/tenant budgets, abuse monitoring und korrekter 429 contract.
Das ist besonders nützlich für SaaS, Public APIs, Webhook Endpoints, AI/Automation-Produkte und Migrationen von Express/Fastify, bei denen alte Limits Tenant, API Keys oder Horizontal Scaling nicht berücksichtigen.
Rate Limiting in Bun.js: in-memory, Redis, Sliding Window und Edge Cases
Verwandte Artikel
AI Assistant Entwicklung Kosten 2026: RAG, Knowledge Base, Integrationen und Support
Praktischer Leitfaden zu Kosten fuer AI Assistants: RAG, Knowledge Base, Channels, Tool Use, Guardrails, Evaluations, Monitoring und Support.
KI fur Landingpage-Entwicklung: wo sie Launches beschleunigt und wo sie Conversion schadet
Eine praxisnahe Analyse zur Nutzung von KI fur Landingpages: v0, Webflow AI, Builder.io, Framer-ahnliche Builder, UX-Generierung, Copy, SEO, Personalisierung, A/B-Tests, Template-Risiken, Accessibility, Security und technischer Schuldenaufbau.
AI SEO / GEO im Jahr 2026: Ihre nächsten Kunden sind nicht Menschen — sondern Agents
Suche verschiebt sich von Klicks zu Antworten. Bots und AI-Agents crawlen, zitieren, empfehlen — und kaufen zunehmend. Erfahren Sie, was AI SEO / GEO bedeutet, warum klassisches SEO nicht mehr reicht und wie PAS7 Studio Marken im agentischen Web sichtbar macht.
Der leistungsstärkste Chip von Apple? M5 Pro und M5 Max brechen Rekorde
Eine Analyse zu Apple M5 Pro und M5 Max im März 2026. Wir zeigen, warum diese Chips als die stärksten professionellen Laptop-SoCs von Apple gelten können, wie sie sich gegen M4 Pro, M4 Max, M1 Pro, M1 Max schlagen und was der Vergleich mit aktuellen Intel- und AMD-Chips zeigt.
Professionelle Entwicklung für Ihr Geschäft
Wir erstellen moderne Web-Lösungen und Bots für Unternehmen. Erfahren Sie, wie wir Ihnen helfen können, Ihre Ziele zu erreichen.