IP-basiertes Rate Limiting in ASP.NET Core (.NET 8): 429 Too Many Requests
Begrenzen Sie Requests pro IP mit dem eingebauten Rate Limiting in .NET 8 und geben Sie bei Überschreitung 429 inkl. Retry-After zurück.
Bei öffentlichen APIs sind Request-Spikes eines der häufigsten Probleme. Ein Bot, ein fehlerhafter Client oder ein zu aggressives Retry-Verhalten kann innerhalb kürzester Zeit sehr viele Requests erzeugen. Eine der einfachsten Schutzmaßnahmen ist Rate Limiting: pro Zeitfenster wird nur eine bestimmte Anzahl an Requests zugelassen.
Mit .NET 8 lässt sich das modern und ohne eigene Middleware umsetzen – über die eingebaute
Rate-Limiting-Middleware.
In diesem Beispiel setzen wir ein IP-basiertes Limit und geben bei Überschreitung
429 Too Many Requests zurück. Zusätzlich fügen wir den Header Retry-After hinzu, damit Clients wissen,
wann sie es erneut versuchen sollen.
Ziel
- Requests pro IP-Adresse begrenzen.
- Bei Überschreitung
429 Too Many Requestszurückgeben. Retry-Afterhinzufügen, damit Clients sauber „backoff“ können.- Das Setup einfach halten und auf Endpoints wiederverwenden.
Program.cs (Fixed Window Rate Limiting pro IP)
Diese Konfiguration erlaubt 60 Requests pro Minute und zwar pro IP.
Wird das Limit überschritten, gibt die Middleware 429 zurück – wir ergänzen zusätzlich Retry-After.
using System.Threading.RateLimiting;
using Microsoft.AspNetCore.RateLimiting;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddRateLimiter(options =>
{
// Standard-Statuscode für abgelehnte Requests
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
// Policy-Name: "ip"
options.AddPolicy("ip", httpContext =>
{
var ip = GetClientIp(httpContext);
// Partition nach IP: jede IP bekommt ihren eigenen Limiter
return RateLimitPartition.GetFixedWindowLimiter(
partitionKey: ip,
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 60, // 60 Requests
Window = TimeSpan.FromMinutes(1), // pro 1 Minute
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 0 // keine Queue: sofort ablehnen
});
});
// Bei Ablehnung Retry-After hinzufügen
options.OnRejected = async (context, ct) =>
{
context.HttpContext.Response.Headers["Retry-After"] = "60";
await context.HttpContext.Response.WriteAsync("Zu viele Anfragen.", ct);
};
});
var app = builder.Build();
app.UseRateLimiter();
app.MapControllers()
.RequireRateLimiting("ip");
app.Run();
static string GetClientIp(HttpContext httpContext)
{
// Wenn Sie hinter einem Reverse Proxy laufen, siehe Hinweis weiter unten.
return httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown";
}
Wo sollte die Policy angewendet werden?
Im Beispiel oben wird Rate Limiting für alle Controller über
MapControllers().RequireRateLimiting("ip") aktiviert.
Wenn Sie es nur für bestimmte Endpoints möchten, können Sie es endpoint-spezifisch konfigurieren.
Schneller Test
Sie können einen Request-Burst schnell simulieren (Linux/macOS):
for i in {1..80}; do
curl -s -o /dev/null -w "%{http_code}\n" https://localhost:5001/api/customers
done
Nach dem 60. Request (innerhalb einer Minute) sollten 429-Antworten erscheinen.
Wichtiger Hinweis: Reverse Proxy und echte Client-IP
Läuft Ihre API hinter einem Reverse Proxy (NGINX, Cloudflare, Azure App Gateway usw.), kann
RemoteIpAddress die IP des Proxys statt der echten Client-IP enthalten.
Dann funktioniert IP-basiertes Rate Limiting nicht korrekt, wenn Sie keine Forwarded Headers konfigurieren.
In ASP.NET Core können Sie Forwarded Headers wie folgt aktivieren (in Produktion nur vertrauenswürdige Proxys zulassen):
using Microsoft.AspNetCore.HttpOverrides;
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// WICHTIG: In Produktion bekannte Proxy-IPs / Netzwerke eintragen
// options.KnownProxies.Add(IPAddress.Parse("10.0.0.10"));
});
var app = builder.Build();
app.UseForwardedHeaders();
Sobald Forwarded Headers korrekt konfiguriert sind, spiegelt RemoteIpAddress die echte Client-IP wider.
Häufige Verbesserungen
- Unterschiedliche Limits pro Endpoint (z. B. strengere Limits für Login).
- Interne IPs whitelisten oder Health-Checks ausnehmen.
- Bei mehreren Instanzen einen distributed store nutzen (In-Memory-Limits gelten pro Instanz).
- Statt Plain-Text eine strukturierte Fehlermeldung zurückgeben (z. B. ProblemDetails).
TL;DR
- IP-basiertes Rate Limiting ist ein einfacher Schutz gegen Bursts und Missbrauch.
- .NET 8 bietet dafür eingebautes Rate Limiting via
AddRateLimiter()+UseRateLimiter(). - Bei Überschreitung
429zurückgeben undRetry-Aftersetzen. - Hinter einem Proxy müssen Forwarded Headers konfiguriert werden, um die echte Client-IP zu erhalten.