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:
Advantages:
Simple to configure
Low overhead
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
| Parameter | Fixed Window | Sliding Window | Token Bucket | Concurrency Limiter |
|---|
| Traffic Smoothing | Low | Medium | High | Not time-based |
| Burst Handling | Poor | Moderate | Excellent | Not applicable |
| Configuration Simplicity | High | Medium | Medium | High |
| Best Use Case | Simple APIs | Public APIs | Variable traffic | CPU-heavy endpoints |
| Resource Control | Time-based | Time-based | Time-based | Active 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.