Yükleniyor...

ASP.NET Core (.NET 8) IP Bazlı Rate Limiting: 429 Too Many Requests

.NET 8’in yerleşik rate limiting altyapısıyla IP başına istek sınırı koyun ve aşımda 429 + Retry-After döndürerek API’nizi koruyun.

Public API’lerde en sık yaşanan sorunlardan biri, istemeden veya kötü niyetli şekilde oluşan istek patlamalarıdır. Basit bir bot, hatalı bir istemci ya da agresif bir “retry” mantığı kısa sürede binlerce istek üretebilir. Bu tarz durumlarda ilk savunma katmanlarından biri rate limiting (istek sınırlandırma) uygulamaktır.

.NET 8 ile bunu “modern” şekilde, yerleşik Rate Limiting middleware ile kurabilirsiniz. Bu örnekte IP bazlı bir limit uygulayıp limit aşıldığında 429 Too Many Requests döndürüyoruz. Ayrıca istemciye “ne zaman tekrar denemeli” bilgisini vermek için Retry-After header’ını ekliyoruz.


Amaç


Program.cs (IP Bazlı Fixed Window Rate Limiting)

Bu ayar, her IP için dakikada 60 istek izni verir. Limit aşıldığında middleware 429 döndürür ve biz de Retry-After header’ını ekleriz.


using System.Threading.RateLimiting;
using Microsoft.AspNetCore.RateLimiting;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddRateLimiter(options =>
{
    // Reddedilen istekler için default status code
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;

    // Policy adı: "ip"
    options.AddPolicy("ip", httpContext =>
    {
        var ip = GetClientIp(httpContext);

        // IP’ye göre partition: her IP kendi limiter’ına sahip olur.
        return RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: ip,
            factory: _ => new FixedWindowRateLimiterOptions
            {
                PermitLimit = 60,                 // 60 istek
                Window = TimeSpan.FromMinutes(1), // 1 dakika içinde
                QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
                QueueLimit = 0                    // kuyruk yok: anında reddet
            });
    });

    // Reddedilince Retry-After ekle
    options.OnRejected = async (context, ct) =>
    {
        context.HttpContext.Response.Headers["Retry-After"] = "60";
        await context.HttpContext.Response.WriteAsync("Çok fazla istek.", ct);
    };
});

var app = builder.Build();

app.UseRateLimiter();

app.MapControllers()
   .RequireRateLimiting("ip");

app.Run();

static string GetClientIp(HttpContext httpContext)
{
    // Reverse proxy arkasındaysanız, alttaki not bölümüne bakın.
    return httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown";
}

Policy’yi Nerede Uygulamalı?

Yukarıdaki örnekte rate limiting’i tüm controller’lara MapControllers().RequireRateLimiting("ip") ile uyguladık. Sadece belirli endpoint’lerde uygulamak isterseniz, endpoint bazlı konfigürasyon veya attribute yaklaşımını tercih edebilirsiniz.


Hızlı Test

Kısa sürede çok sayıda istek atarak limiti test edebilirsiniz (Linux/macOS):


for i in {1..80}; do
  curl -s -o /dev/null -w "%{http_code}\n" https://localhost:5001/api/customers
done

60’ıncı isteğin ardından (aynı dakika içinde) 429 yanıtlarını görmeniz gerekir.


Önemli Not: Reverse Proxy ve Gerçek IP

Eğer API’niz bir reverse proxy arkasındaysa (NGINX, Cloudflare, Azure App Gateway vb.), RemoteIpAddress gerçek istemci IP’si yerine proxy IP’sini gösterebilir. Bu durumda IP bazlı rate limiting doğru çalışmaz. Çözüm olarak forwarded headers ayarlamanız gerekir.

ASP.NET Core’da forwarded headers’ı şu şekilde etkinleştirebilirsiniz (Production’da sadece güvenilir proxy’leri tanımlayın):


using Microsoft.AspNetCore.HttpOverrides;

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;

    // ÖNEMLİ: Production'da bilinen proxy IP'lerini / network'lerini ekleyin
    // options.KnownProxies.Add(IPAddress.Parse("10.0.0.10"));
});

var app = builder.Build();
app.UseForwardedHeaders();

Forwarded headers doğru yapılandırıldığında RemoteIpAddress gerçek istemci IP’sini yansıtır.


Sık Yapılan İyileştirmeler


TL;DR

  • IP bazlı rate limiting, API’yi istek patlamalarına ve kötüye kullanıma karşı korumanın basit bir yoludur.
  • .NET 8’de AddRateLimiter() + UseRateLimiter() ile yerleşik olarak uygulanır.
  • Limit aşıldığında 429 ve Retry-After döndürerek istemciyi doğru yönlendirin.
  • Proxy arkasındaysanız gerçek IP için forwarded headers yapılandırmasını unutmayın.