If you are building APIs today, you already know that putting your application on the internet is like opening a new store. You want customers, but you definitely don’t want a chaotic mob rushing the doors and crashing the building.
This is exactly where Rate Limiting comes into play. As a .NET Architect, I’ve seen firsthand how a lack of rate limiting can bring down entire microservices under heavy load. In this post, we’ll break down what rate limiting is in simple terms, why your APIs desperately need it, and how to implement it elegantly in the latest ASP.NET.
What is Rate Limiting? (The Layman’s Explanation)
Imagine an exclusive nightclub. The bouncer at the door has a few rules:
Only 50 people are allowed inside at any given time.
A single VIP member can only bring in 3 guests per hour.
Rate limiting works exactly like that bouncer, but for your web application. It is a technique used to control the amount of incoming traffic to a network, server, or API. It restricts how many requests a user (or an IP address) can make in a given timeframe.
If a user tries to make 100 requests in a minute when the limit is 20, the server will process the first 20 and then politely (or impolitely) reject the rest, usually returning an HTTP 429 Too Many Requests status code.
Why Do We Need Rate Limiting? (The Advantages)
Implementing rate limiting isn’t just a “nice-to-have”; it’s a critical architectural requirement for modern APIs. Here is why:
Defending Against DDoS Attacks: Malicious bots might try to overwhelm your server with millions of requests. Rate limiting stops them in their tracks before your server’s CPU hits 100%.
Preventing “Noisy Neighbor” Problems: In a multi-tenant system, you don’t want one highly active user consuming all the server resources and slowing down the application for everyone else. Rate limiting ensures fair usage.
Controlling Cloud Costs: If your API triggers a serverless function, a database read, or a paid third-party API (like an LLM or SMS gateway), unthrottled requests can lead to astronomical cloud bills.
Improving Application Stability: It prevents your microservices from being pushed beyond their breaking point, keeping your system highly available.
The 4 Built-In Rate Limiting Algorithms in ASP.NET
Since .NET 7, Microsoft introduced a native Microsoft.AspNetCore.RateLimiting middleware. In .NET 8, it’s more robust than ever. Let’s look at the four algorithms it provides:
Fixed Window: Like a daily data cap on your mobile plan. You get 100 requests per minute. At the start of the next minute, the counter resets to zero.
Sliding Window: A smarter Fixed Window. Instead of resetting abruptly at the start of a new minute, it looks back at the last 60 seconds from the exact moment of the request. This prevents traffic spikes at the exact boundary of the time window.
Token Bucket: Imagine a bucket that holds a maximum of 10 tokens. Every minute, 2 new tokens are dropped into the bucket. Each API request costs 1 token. If the bucket is empty, the request is rejected. This allows for short bursts of traffic while maintaining a steady long-term rate.
Concurrency Limit: This doesn’t care about time. It only cares about simultaneous requests. Think of a fitting room in a clothing store: only 5 people can be trying on clothes at the same time. If a 6th person arrives, they must wait until someone leaves.
How to Implement Rate Limiting in ASP.NET
Let’s get our hands dirty with some code. Here is how you can set up a Fixed Window rate limiter in your latest ASP.NET Web API (using minimal APIs for simplicity).
Step 1: Configure the Rate Limiter Service
Open your Program.cs file. We need to register the rate limiting services and define a policy. Let’s create a policy called "BasicLimiter" that allows 5 requests every 10 seconds.
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
var builder = WebApplication.CreateBuilder(args);
// 1. Add Rate Limiting Services
builder.Services.AddRateLimiter(options =>
{
// Define a Fixed Window Policy
options.AddFixedWindowLimiter("BasicLimiter", opt => {
opt.Window = TimeSpan.FromSeconds(10); // Per 10 seconds
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
opt.QueueLimit = 2; // Allow 2 requests to wait in queue before rejecting
});
// Customizing the rejection response (Optional but recommended)
options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
await context.HttpContext.Response.WriteAsync("Whoa there! You are sending requests too fast. Please slow down.", cancellationToken: token);
};
});
var app = builder.Build();
Step 2: Add the Middleware
Right after building the app, you need to add the rate limiting middleware to the request pipeline. Make sure you place this before mapping your endpoints.
// 2. Add the middleware to the pipeline
app.UseRateLimiter();
Step 3: Apply the Policy to Your Endpoints
Now, you simply tell your endpoints which rate-limiting policy to use.
For Minimal APIs:
app.MapGet("/api/inventory", () => "Here is your inventory data!")
.RequireRateLimiting("BasicLimiter"); // Apply the policy here
For Controller-Based APIs:
If you are using standard controllers, you just decorate your controller or action method with an attribute:
[ApiController]
[Route("[controller]")]
[EnableRateLimiting("BasicLimiter")] // Apply to the whole controller
public class InventoryController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok("Here is your inventory data!");
}
[HttpGet("unlimited")]
[DisableRateLimiting] // Opt-out a specific endpoint
public IActionResult GetUnlimited()
{
return Ok("No limits here!");
}
}
Wrapping Up
Adding rate limiting to your ASP.NET applications is no longer a complex task requiring third-party libraries. With the native middleware, you can protect your APIs, save on cloud costs, and ensure a smooth experience for all your users with just a few lines of code.
Whether you are building a simple blog backend or a massive multi-tenant microservice architecture, putting a “bouncer” at the door of your API is an architectural best practice you shouldn’t skip.
Happy Coding 🙂