ASP.NET Core's rate limiter partitions however you key it. Key it on the caller, and one client's burst stops being every other client's problem.
I built a small lab to get that wiring right end to end.
Four stages:
Authenticate. A custom header scheme validates the client and issues a client_id claim. Nothing downstream can partition per client until this exists.
Partition. The policy reads that claim and keys the partition by client id plus HTTP verb. A client's read budget and write budget are separate buckets, and so are two different clients'.
Limit. Fixed window: 10 requests per 10 seconds, queue limit 0. No waiting in line, you either get a permit or you don't.
Enforce. Request 11 in the window gets HTTP 429.
The client's profile decides which partition it lands in:
Standard: limited on every verb.
Premium: reads skip the limiter, writes still get 10 per 10 seconds.
Unlimited: no limiter at all.
The part worth remembering is the ordering.
UseRateLimiter() has to run after UseAuthentication() and UseAuthorization(). Register it earlier and the policy executes before the client_id claim exists, so there is nothing to partition on. In this implementation that throws rather than quietly limiting on nothing, which is the failure mode you want: loud, not silent.
Three calls do the wiring: AddRateLimiter() to register the policy, UseRateLimiter() to put it in the pipeline, RequireRateLimiting() to attach it to an endpoint group.
Code, including the in-memory client fixtures and the Todo endpoints it protects: https://github.com/sthotakura/rate-limiter-lab
Top comments (0)