Wird geladen...

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


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


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 429 zurückgeben und Retry-After setzen.
  • Hinter einem Proxy müssen Forwarded Headers konfiguriert werden, um die echte Client-IP zu erhalten.