As APIs become the backbone of modern applications, protecting them against abuse, denial-of-service attacks, and excessive requests is critical. Rate limiting is a widely used strategy that controls how many requests a client can make within a specific time window. Implementing rate limiting ensures fair usage, improves performance, and protects backend services from overload.

In this article, we will explore how to implement rate limiting in ASP.NET Core APIs using real-world practices, focusing on maintainability, scalability, and production readiness.

Table of Contents

  1. Introduction to Rate Limiting

  2. Benefits of Rate Limiting

  3. Common Rate Limiting Strategies

  4. Architecture Overview

  5. Setting Up ASP.NET Core API Project

  6. Implementing Rate Limiting Using Middleware

  7. Using ASP.NET Core Built-in Rate Limiting

  8. Advanced Strategies: IP-Based and User-Based Limits

  9. Logging, Monitoring, and Alerts

  10. Best Practices for Production

  11. Testing and Performance Considerations

  12. Conclusion

1. Introduction to Rate Limiting

Rate limiting restricts the number of requests a client can send to a server in a given time period. It is essential to:

Without proper rate limiting, APIs can be vulnerable to DDoS attacks or accidental spikes, affecting the availability for legitimate users.

2. Benefits of Rate Limiting

Some key benefits include:

3. Common Rate Limiting Strategies

There are several strategies to implement rate limiting:

  1. Fixed Window: Allows a fixed number of requests per time window. Simple but can burst at window boundaries.

  2. Sliding Window: Uses a moving window for smoother rate limiting.

  3. Token Bucket: Tokens are added at a fixed rate; requests consume tokens. Allows short bursts.

  4. Leaky Bucket: Requests are processed at a steady rate; excess requests are queued or dropped.

For APIs, Token Bucket and Sliding Window are commonly preferred due to their flexibility.

4. Architecture Overview

A scalable rate limiting architecture involves:

In production, Redis or distributed caches are preferred to handle multiple server instances.

5. Setting Up ASP.NET Core API Project

Start by creating an ASP.NET Core Web API:

dotnet new webapi -n RateLimitAPI
cd RateLimitAPI

Add Redis (optional for distributed rate limiting):

dotnet add package StackExchange.Redis

6. Implementing Rate Limiting Using Middleware

Custom middleware is a flexible way to implement rate limiting.

a. Create Rate Limiting Middleware

public class RateLimitingMiddleware
{
    private readonly RequestDelegate _next;
    private static readonly MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());
    private readonly int _limit = 5; // Max requests
    private readonly TimeSpan _period = TimeSpan.FromMinutes(1);

    public RateLimitingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var ipAddress = context.Connection.RemoteIpAddress.ToString();
        var cacheKey = $"rl_{ipAddress}";

        if (!_cache.TryGetValue(cacheKey, out int requestCount))
        {
            requestCount = 0;
        }

        if (requestCount >= _limit)
        {
            context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
            await context.Response.WriteAsync("Rate limit exceeded. Try again later.");
            return;
        }

        _cache.Set(cacheKey, requestCount + 1, _period);
        await _next(context);
    }
}

b. Register Middleware

app.UseMiddleware<RateLimitingMiddleware>();

This simple middleware enforces a limit of 5 requests per minute per IP address.

7. Using ASP.NET Core Built-in Rate Limiting

Starting from .NET 7, ASP.NET Core has built-in rate limiting with Microsoft.AspNetCore.RateLimiting package.

a. Install Package

dotnet add package Microsoft.AspNetCore.RateLimiting

b. Configure Rate Limiting in Program.cs

using Microsoft.AspNetCore.RateLimiting;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("Fixed", config =>
    {
        config.PermitLimit = 10;
        config.Window = TimeSpan.FromMinutes(1);
        config.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        config.QueueLimit = 2;
    });
});

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

c. Apply to Specific Endpoints

app.MapGet("/api/data", () => "Hello World").RequireRateLimiting("Fixed");

The built-in solution is production-ready, supports queuing, and can integrate with distributed caches.

8. Advanced Strategies: IP-Based and User-Based Limits

a. IP-Based Rate Limiting

b. User-Based Rate Limiting

Example using Redis

var count = await redis.StringIncrementAsync($"rl_{userId}");
if (count == 1)
    await redis.KeyExpireAsync($"rl_{userId}", TimeSpan.FromMinutes(1));

if (count > 100)
{
    context.Response.StatusCode = 429;
    await context.Response.WriteAsync("Rate limit exceeded");
    return;
}

9. Logging, Monitoring, and Alerts

To manage rate limits effectively, logging and monitoring are crucial:

_logger.LogWarning("Rate limit exceeded for IP {IP}", context.Connection.RemoteIpAddress);

10. Best Practices for Production

  1. Use distributed caches like Redis for multi-server environments.

  2. Different limits for different endpoints: Sensitive operations can have stricter limits.

  3. Return appropriate headers: Inform clients of remaining requests.

    • X-RateLimit-Limit

    • X-RateLimit-Remaining

    • Retry-After

  4. Graceful degradation: Queue requests or provide fallback messages instead of outright rejecting.

  5. Combine with authentication: Use API keys or JWTs to enforce per-user limits.

11. Testing and Performance Considerations

Conclusion

Rate limiting is a critical component of secure and stable APIs. By controlling request rates, you protect your system from abuse, improve performance, and ensure fair usage. ASP.NET Core provides both custom middleware options and built-in rate limiting features that are production-ready.

Key takeaways

With a well-implemented rate limiting strategy, your APIs can handle high traffic securely, reliably, and efficiently.