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ç
- İstekleri IP adresi bazında sınırlamak.
- Limit aşıldığında
429 Too Many Requestsdöndürmek. - İstemciyi yönlendirmek için
Retry-Aftereklemek. - Kurulumu basit tutup endpoint’lerde tekrar kullanılabilir yapmak.
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
- Endpoint bazlı farklı limitler uygulayın (ör. login için daha sıkı limit).
- İç ağ IP’lerini whitelist’e alın veya health check endpoint’lerini hariç tutun.
- Birden fazla instance çalıştırıyorsanız distributed store düşünün (in-memory limitler instance başınadır).
- Plain text yerine yapılandırılmış bir hata formatı döndürün (ör. ProblemDetails).
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
429veRetry-Afterdöndürerek istemciyi doğru yönlendirin. - Proxy arkasındaysanız gerçek IP için forwarded headers yapılandırmasını unutmayın.