ASP.NET Core  

How to Implement Rate Limiting in ASP.NET Core 8?

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:

  • APIs can be overwhelmed by excessive requests

  • Systems become vulnerable to brute-force attacks

  • Infrastructure costs increase

  • Legitimate users experience degraded performance

With rate limiting:

  • Traffic is controlled per user/IP/client

  • API abuse is reduced

  • Backend systems remain stable

  • Fair usage policies are enforced

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:

  • Fixed Window

  • Sliding Window

  • Token Bucket

  • Concurrency Limiter

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:

  • 5 requests every 10 seconds

  • 2 additional queued requests

  • Oldest queued request processed first

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:

  • 5 requests per 10 seconds

  • Counter resets after 10 seconds

Advantages:

  • Simple to configure

  • Low overhead

Disadvantages:

  • Traffic spikes at window boundaries

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:

  • Smoother traffic distribution

  • Reduces burst behavior

Disadvantages:

  • Slightly more complex

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:

  • Allows short bursts

  • Ideal for APIs with variable traffic

Disadvantages:

  • Slightly more configuration required

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:

  • Heavy processing endpoints

  • CPU-intensive operations

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

  • Apply stricter limits to authentication endpoints

  • Use distributed rate limiting for multi-instance deployments

  • Combine with API gateway rate limiting

  • Monitor rate limit metrics

  • Avoid overly aggressive limits

  • Document rate limit policies for API consumers

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

Real-World Example

Consider a payment API:

  • Limit login attempts to 5 per minute

  • Limit transaction submissions to 10 per minute

  • Restrict heavy report generation endpoints using concurrency limiter

This prevents abuse and protects financial operations.

Common Mistakes

  • Applying global limits without considering endpoint sensitivity

  • Ignoring distributed deployments

  • Not returning meaningful error messages

  • Not monitoring rate limit violations

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.