Sarıkaya Dev Logo

Implement Distributed Rate Limiting in .NET 8 Minimal APIs with AspNetCoreRateLimit and Redis

Mahmut Sarıkaya 3 min read 3 Views 0
Implement Distributed Rate Limiting in .NET 8 Minimal APIs with AspNetCoreRateLimit and Redis

Ever noticed how a sudden traffic spike can bring your API to a halt?

Why Distributed Rate Limiting Matters

In micro‑service environments a single instance cannot reliably enforce request caps when the load is shared across multiple nodes. Without a coordinated strategy, attackers can bypass limits by targeting different pods, and legitimate users suffer inconsistent throttling. A distributed store such as Redis guarantees that every request checks the same counter, keeping the policy uniform across the whole cluster.

Prerequisites and System Requirements

You need .NET 8 SDK, a running Redis server (minimum version 6.0), and the AspNetCoreRateLimit NuGet package. The Redis instance should be reachable from all API nodes; for local testing Docker provides a quick setup:

docker run -d --name redis-rate -p 6379:6379 redis:6-alpine

Adding Redis to a .NET 8 Minimal API

Register the StackExchange.Redis cache early in the builder pipeline so that the rate‑limiting services can reuse it.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddStackExchangeRedisCache(options => {
    options.Configuration = "localhost:6379";
    options.InstanceName = "RateLimitRedis_";
});

Installing AspNetCoreRateLimit

Run the following command in the project folder. The package includes both IP‑based and client‑ID based policies.

dotnet add package AspNetCoreRateLimit

Configuring Rate Limiting Options

Create an appsettings.json section that defines a general rule of 100 requests per minute for every endpoint. The same file can host client‑specific limits if needed.

{
  "IpRateLimiting": {
    "EnableEndpointRateLimiting": true,
    "StackBlockedRequests": false,
    "RealIpHeader": "X-Real-IP",
    "ClientIdHeader": "X-ClientId",
    "HttpStatusCode": 429,
    "GeneralRules": [
      {
        "Endpoint": "*",
        "Period": "1m",
        "Limit": 100
      }
    ]
  }
}

Bind the configuration in Program.cs and switch the in‑memory store for the Redis implementation.

builder.Services.Configure<IpRateLimitOptions>(builder.Configuration.GetSection("IpRateLimiting"));
builder.Services.Configure<IpRateLimitPolicies>(builder.Configuration.GetSection("IpRateLimitPolicies"));
builder.Services.AddMemoryCache(); // required by the library
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
builder.Services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();
// Use Redis as the distributed counter store
builder.Services.AddSingleton<IRateLimitCounterStore, RedisRateLimitCounterStore>();
builder.Services.AddInMemoryRateLimiting(); // keeps rule parsing in memory

Applying the Middleware to Minimal APIs

Insert the middleware before any endpoint mapping so that each request is evaluated first.

var app = builder.Build();
app.UseIpRateLimiting();
app.MapGet("/weather", () => new[] { "Sunny", "Rainy", "Cloudy" });
app.MapGet("/status", () => Results.Ok(new { uptime = "24h", version = "1.0.0" }));
app.Run();

Testing the Limits Locally

Use curl in a loop to fire 120 requests against /weather. After the 100th request the server returns HTTP 429 with the default body “Too Many Requests”.

for i in $(seq 1 120); do curl -s -o /dev/null -w "%{http_code}\n" http://localhost:5000/weather; done

The output shows a series of 200 codes followed by 429 codes, confirming that the distributed counter is shared across the process and, when scaled out, across all nodes.

Best Practices for Production

1. Set StackBlockedRequests to true if you want blocked attempts to count toward the quota, preventing burst attacks that alternate between allowed and blocked calls.
2. Use a dedicated Redis instance with persistence turned on; a volatile cache can lose counters after a restart, temporarily lifting limits.
3. Monitor the RateLimit-* keys in Redis; a high key churn may indicate a mis‑configured period or an abusive client.
4. Combine IP limits with client‑ID limits for APIs that require authentication, ensuring that a compromised key cannot flood the service.

Conclusion

By pairing AspNetCoreRateLimit with Redis, .NET 8 Minimal APIs gain a robust, horizontally scalable throttling layer. The approach requires only a few lines of configuration, yet it protects your services from overload, improves fairness, and provides clear metrics through Redis. Implement it early, tune the limits to your traffic profile, and let the distributed store keep your API responsive under any load.

Sources

  • Microsoft Docs – ASP.NET Core Rate Limiting
  • StackExchange.Redis Documentation
  • AspNetCoreRateLimit GitHub Repository

Author: Mahmut Sarıkaya — sarikayadev.com

Tags: #.NET 8 #minimal APIs #rate limiting #AspNetCoreRateLimit #Redis
Share:
M

Written by

Mahmut Sarıkaya

Software Developer

Comments

No comments yet. Be the first to share your thoughts!

Leave a Comment

2 + 4 =