How to Add Request Throttling and Rate Limiting in ASP.NET Core

Configure ASP.NET Core rate-limiting middleware with named policies, endpoint attributes, HTTP 429 responses, and production considerations.

Article guideContents, topics, tags, and RSS
Get new notes via RSS

ASP.NET Core includes rate-limiting middleware, so a current application does not need a custom action filter backed by a process-local memory cache. The middleware supports fixed-window, sliding-window, token-bucket, and concurrency limiters, plus named policies that can be applied to selected endpoints.

Rate limiting protects capacity and makes abusive or accidental traffic predictable. It is not a replacement for authentication, authorization, input validation, upstream network protection, or a dedicated DDoS service.

Register a named policy

This fixed-window policy allows five requests every ten seconds and rejects excess requests immediately:

using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;

    options.AddFixedWindowLimiter("short-window", limiter =>
    {
        limiter.PermitLimit = 5;
        limiter.Window = TimeSpan.FromSeconds(10);
        limiter.QueueLimit = 0;
        limiter.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        limiter.AutoReplenishment = true;
    });
});

var app = builder.Build();

app.UseRouting();
app.UseRateLimiter();
app.MapControllers();

app.Run();

With endpoint-specific policies, call UseRateLimiter after UseRouting. The current ASP.NET Core rate-limiting documentation covers middleware order and the available algorithms in detail.

Apply the policy to an endpoint

Use EnableRateLimitingAttribute on a controller or action:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;

[ApiController]
[Route("api/[controller]")]
public class ActionsController : ControllerBase
{
    [HttpGet("limited")]
    [EnableRateLimiting("short-window")]
    public IActionResult GetLimited()
    {
        return Ok(new { message = "OK" });
    }
}

You can also attach a policy while mapping an endpoint:

app.MapGet("/api/limited", () => Results.Ok(new { message = "OK" }))
    .RequireRateLimiting("short-window");

When the limit is exceeded, the middleware returns 429 Too Many Requests. A client should treat that response as a signal to slow down, not immediately retry in a tight loop.

Partition limits deliberately

A single global counter is rarely the right production policy. Depending on the endpoint, partition requests by an authenticated account, API key, tenant, client application, or another trustworthy identifier.

An IP address can be useful for coarse anonymous limits, but proxies, carrier-grade NAT, IPv6 privacy addresses, and spoofable forwarding headers make it an imperfect identity. Only use forwarded headers after configuring trusted proxies.

For a multi-instance application, remember that an in-process limiter controls each application instance independently. If the business requirement is a strict shared quota, enforce it at a shared gateway or use infrastructure designed for distributed limits.

Choose the right algorithm

  • Fixed window: simple quotas such as 100 requests per minute, with bursts possible at the boundary.
  • Sliding window: smoother behavior around window boundaries.
  • Token bucket: allows controlled bursts while replenishing capacity over time.
  • Concurrency: limits simultaneous work rather than requests over a period.

Start with a measurable capacity or product rule, log rejections, and tune the policy from real traffic. A very low limit added without observing legitimate clients can turn your protection into an outage.

Johan Boström smiling in a navy jacket and white shirt.
Author portrait

About the author

Johan Boström

Technology leader · Solution architect · Developer

My primary work is leading developers and working as a solution architect. I still develop when hands-on work helps the team or the solution.

I mainly work across systems, integrations, and AI. This archive keeps the practical details, lessons, and implementation notes worth finding again.