Safeguard
Security Guides•

Which ASP.NET Core rate limiter should you use?

ASP.NET Core ships fixed window, sliding window, token bucket and concurrency limiters. Choose by endpoint cost, and partition with care to avoid DoS.

Safeguard Security Team
4 min read

ASP.NET Core's rate limiting middleware offers four limiters: fixed window, sliding window, token bucket and concurrency. Use fixed window for simple per client caps, token bucket when short bursts are fine, sliding window for smoother enforcement, and concurrency for expensive endpoints. Choose by how costly the endpoint is.

What the middleware is

The Microsoft.AspNetCore.RateLimiting middleware lets an app define rate limiting policies and attach them to endpoints. You register policies with AddRateLimiter, turn the middleware on with UseRateLimiter, and attach a named policy with RequireRateLimiting or the EnableRateLimiting attribute. A global limiter set through options applies to all endpoints automatically.

The four algorithms

LimiterHow it limitsSuits
Fixed windowA set number of requests per time window, then the count resetsSimple caps such as login attempts
Sliding windowLike fixed window but the window is split into segments, and requests from the expired segment are added back as it slidesSmoother limits without the burst at a window edge
Token bucketA fixed number of tokens added each replenishment period, up to a bucket limitAPIs that allow bursts but control the average
ConcurrencyLimits only simultaneous requests, not requests per periodCostly endpoints such as reports or exports

The docs note that the fixed, sliding and token limiters cap requests over a period, while the concurrency limiter does not. They also advise considering endpoint cost, meaning the time, data access, CPU and I/O it uses, when you choose.

Partitioning, and its risk

Partitions give each key its own counter, so one user cannot consume everyone's allowance. Keys can be user identity, IP address, API key or path.

Microsoft's page warns about this directly. If you create partitions from user input, the app becomes vulnerable to denial of service. For example, partitioning on client IP leaves you open to attacks that use source address spoofing. Another note says each distinct key creates and caches its own limiter, so partitioning on unbounded user controlled input can exhaust memory. Prefer authenticated identity where available, and bound the number of keys.

Rejections and responses

The docs show setting RejectionStatusCode to 429 Too Many Requests, or an OnRejected callback that sets the status and a Retry-After header from limiter metadata. Not every limiter provides retry timing. The page notes the concurrency limiter does not.

builder.Services.AddRateLimiter(o =>
{
    o.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
    o.AddFixedWindowLimiter("login", p =>
    {
        p.PermitLimit = 5;
        p.Window = TimeSpan.FromMinutes(1);
        p.QueueLimit = 0;
    });
});

app.UseRateLimiter();
app.MapPost("/login", Login).RequireRateLimiting("login");

One ordering note from the docs: when you use endpoint specific policies or attributes, UseRateLimiter must be called after UseRouting.

It is not DDoS protection

The documentation states that rate limiting helps reduce denial of service risk but is not a comprehensive solution for distributed attacks, and suggests a commercial protection service or the DDoS features of your cloud host alongside it. Treat the middleware as application level fairness and abuse control.

Steps

  1. Rank endpoints by cost and by abuse value, such as login, password reset, search and export.
  2. Pick a limiter per class, and a conservative global default.
  3. Partition by authenticated identity first, then by IP for anonymous traffic.
  4. Return 429 with Retry-After where the limiter supplies it.
  5. Load test, as the docs advise, before deploying.
  6. Review how the client IP is determined behind proxies.

Try it yourself

Click through it at your own pace, or open it full screen. Look for how missing rate limiting appears as an API finding.

Quick check

Should every endpoint use the same limit?

No. Login and password reset need tight limits, while read endpoints can be looser. A global default plus named policies covers both.

Does rate limiting stop credential stuffing?

It slows it. Pair it with multi factor authentication, breached password checks and monitoring.

Safeguard dependency scanning tracks the NuGet packages behind your ASP.NET Core API, and the free scan is at gold.safeguard.sh/scan.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.