Rate limiting in ASP.NET Core 8 is a built-in middleware feature that controls the number of incoming HTTP requests a client can make within a specified time window. It protects APIs from abuse, prevents denial-of-service attacks, ensures fair usage, and improves overall system stability in high-traffic environments.

With the introduction of the native Rate Limiting middleware in ASP.NET Core, developers no longer need third-party libraries for most rate limiting scenarios. This article provides a complete implementation guide, explains different rate limiting algorithms, demonstrates configuration examples, compares strategies, and discusses production best practices.

Why Rate Limiting Is Important

Without rate limiting:

With rate limiting:

Rate limiting is critical for public APIs, authentication endpoints, payment systems, and microservices architectures.

Built-In Rate Limiting in ASP.NET Core 8

ASP.NET Core 8 provides the Microsoft.AspNetCore.RateLimiting middleware, which supports multiple algorithms:

To use rate limiting, you must configure services and middleware in Program.cs.

Step 1: Add Rate Limiting Services

In Program.cs:

using System.Threading.RateLimiting;

builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("FixedPolicy", config =>
    {
        config.PermitLimit = 5;
        config.Window = TimeSpan.FromSeconds(10);
        config.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        config.QueueLimit = 2;
    });
});

This configuration allows:

Step 2: Enable Middleware

Add middleware in the request pipeline:

app.UseRateLimiter();

Step 3: Apply Rate Limiting to Endpoints

Apply the policy globally:

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

Or apply to specific endpoint:

app.MapGet("/limited", () => "Limited endpoint")
   .RequireRateLimiting("FixedPolicy");

Fixed Window Rate Limiting

Fixed Window divides time into fixed intervals.

Example:

Advantages:

Disadvantages:

Sliding Window Rate Limiting

Sliding Window smooths traffic by tracking requests in smaller segments within a rolling window.

Example configuration:

builder.Services.AddRateLimiter(options =>
{
    options.AddSlidingWindowLimiter("SlidingPolicy", config =>
    {
        config.PermitLimit = 10;
        config.Window = TimeSpan.FromSeconds(30);
        config.SegmentsPerWindow = 3;
        config.QueueLimit = 2;
    });
});

Advantages:

Disadvantages:

Token Bucket Rate Limiting

Token Bucket allows bursts of traffic while maintaining a steady refill rate.

Example configuration:

builder.Services.AddRateLimiter(options =>
{
    options.AddTokenBucketLimiter("TokenPolicy", config =>
    {
        config.TokenLimit = 10;
        config.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        config.QueueLimit = 2;
        config.ReplenishmentPeriod = TimeSpan.FromSeconds(5);
        config.TokensPerPeriod = 5;
        config.AutoReplenishment = true;
    });
});

Advantages:

Disadvantages:

Concurrency Limiter

Concurrency limiter restricts simultaneous request processing rather than request rate over time.

builder.Services.AddRateLimiter(options =>
{
    options.AddConcurrencyLimiter("ConcurrencyPolicy", config =>
    {
        config.PermitLimit = 3;
        config.QueueLimit = 2;
    });
});

Useful for:

Rate Limiting Strategies Comparison

ParameterFixed WindowSliding WindowToken BucketConcurrency Limiter
Traffic SmoothingLowMediumHighNot time-based
Burst HandlingPoorModerateExcellentNot applicable
Configuration SimplicityHighMediumMediumHigh
Best Use CaseSimple APIsPublic APIsVariable trafficCPU-heavy endpoints
Resource ControlTime-basedTime-basedTime-basedActive request-based

Selecting the correct strategy depends on workload characteristics.

Applying Rate Limiting Per User or IP

You can configure rate limiting per client IP:

builder.Services.AddRateLimiter(options =>
{
    options.AddPolicy("PerIpPolicy", context =>
        RateLimitPartition.GetFixedWindowLimiter(
            context.Connection.RemoteIpAddress?.ToString() ?? "unknown",
            _ => new FixedWindowRateLimiterOptions
            {
                PermitLimit = 5,
                Window = TimeSpan.FromSeconds(10)
            }));
});

This ensures each IP has its own independent rate limit.

Customizing Rejection Response

You can customize the response when the rate limit is exceeded:

builder.Services.AddRateLimiter(options =>
{
    options.OnRejected = async (context, token) =>
    {
        context.HttpContext.Response.StatusCode = 429;
        await context.HttpContext.Response.WriteAsync("Too many requests.", token);
    };
});

HTTP 429 (Too Many Requests) is the standard response.

Production Best Practices

For distributed systems, consider external stores such as Redis for consistent rate limiting across instances.

Real-World Example

Consider a payment API:

This prevents abuse and protects financial operations.

Common Mistakes

Proper design ensures security without harming legitimate users.

Summary

Implementing rate limiting in ASP.NET Core 8 involves configuring the built-in Rate Limiting middleware, selecting appropriate algorithms such as Fixed Window, Sliding Window, Token Bucket, or Concurrency Limiter, and applying policies at endpoint or global levels. By controlling request frequency, customizing rejection responses, and applying per-user or per-IP limits, developers can protect APIs from abuse, improve stability, and ensure fair usage. Choosing the correct strategy based on traffic patterns and deploying distributed rate limiting for scalable environments ensures production-grade resilience and performance.