Security Guides
Rate limiting and abuse control — counting requests stops neither brute force nor a runaway bill
Rate limiting is not about slowing traffic; it is about deciding who you count, what you count, and what happens at the ceiling. NIST caps consecutive failures at 100 while warning that harsh lockout denies service to real users, and OWASP puts spending limits on the list of API limits.
Rate limiting usually fails not because the limit was too loose but because the wrong question was asked. Before you decide "how many per minute", three questions need answers: who are you counting, what are you counting, and what happens at the ceiling.
Question 1: who are you counting?
Make IP the primary axis and it breaks from both sides
The attacker's side: behind a reverse proxy or CDN, the source address your application sees may be a value written in a header. Trust X-Forwarded-For unconditionally and rewriting a header is enough to become "somebody else" — you believe you are counting, and you are counting nothing. How far that header can be trusted is covered in X-Forwarded-For spoofing and trusted proxies.
The legitimate user's side: corporate NAT and mobile networks mean many unrelated users share one address. Tighten per IP and you catch people who are not attacking you.
So the primary axis should be an identifier tied to the actor — an account, an API key, an established session — with IP layered on as a supporting signal. At unauthenticated entrances (before login) there is no actor to key on, which is what the next two questions have to cover.
Question 2: what are you counting?
A design that counts only requests cannot stop an expensive single request. OWASP's API Security Top 10 spells out what needs a ceiling under API4:2023 "Unrestricted Resource Consumption".
What you are counting
Requests: 100/min → under the limit ✓
↓ but what is actually consumed is
CPU · time
a full scan per call
Memory
an oversized input
Bandwidth
ten thousand records
Money
repeated metered calls
= exhausted without ever hitting the ceiling
- Time and memory
- Execution timeouts, maximum memory allocation
- OS resources
- Number of file descriptors, number of processes
- Input size
- Maximum upload file size, maximum size of input parameters
- Work per request
- Number of operations performed in a single API client request, records returned per page
- Money
- Spending limits for third-party providers and API integrations (see below — this is the last line)
Holding to "100 requests per minute" protects nothing if one request returns ten thousand records, processes a huge file, or makes several calls to a metered external API. Match the unit you count to the thing you are protecting — CPU, memory, bandwidth, money. That is the core of the design.
Question 3: what happens at the ceiling?
This is the most misunderstood part. Stricter is not automatically safer.
The side effect of blunt lockout
"Three failures and the account is frozen" feels right, but it also means an attacker can deliberately freeze other people's accounts — you have turned lockout into a denial-of-service tool aimed at your own users. You stopped brute force by shipping a way to shut people out.
Designing for "not worth it"
Take away speed rather than access. What NIST SP 800-63B lists: requiring a CAPTCHA before authentication, a wait after a failed attempt that grows as the account approaches its maximum allowance, an allow-list of IP addresses the subscriber has authenticated from before, and risk-based or adaptive techniques that judge whether behaviour falls within normal ranges (address, geolocation, timing, browser metadata).
NIST's number is larger than you expect: 100 consecutive failures
NIST SP 800-63B states that "the verifier SHALL limit consecutive failed authentication attempts on a single account to no more than 100". The familiar "lock after three" is not what the standard asks for.
Why 100 is enough: once delays and a CAPTCHA are in the path, reaching 100 stops being practical in any useful timeframe. Put the other way round — tightening the count while adding no delay is the worst of both: it shuts legitimate users out while still letting an automated attack burn through its attempts quickly.
One more practical pointer from the same document: on successful authentication, verifiers SHOULD disregard previous failed attempts for that user from the same IP address. Decide when the counter resets, too — that is part of the design.
Where to apply what
Login (brute force and credential stuffing)
Make the per-account failure counter the primary axis and grow the delay progressively. Use IP as a supporting signal, never as the sole basis. Adding multi-factor authentication means a correct password does not immediately grant access, which breaks the economics of guessing altogether. Note also that recent intrusions increasingly arrive with valid credentials rather than guessing them (the 2026 entry-path analysis) — rate limiting is necessary, not sufficient.
Password resets and confirmation email (mail bombing)
Count both what can trigger a send and how many go to the same destination. A form that accepts somebody else's address lets an attacker harass a third party and damage your sending reputation at once. Combine a per-destination cooldown, a minimum resend interval, and deletion of records left unconfirmed (which also shrinks the unverified personal data you hold). The reset path itself is covered in password reset design flaws.
Public endpoints and AI features (runaway cost)
Count cost, not requests. Cap input size, limit the operations performed per request, and always configure the provider-side spending limit. OWASP's scenarios include a bill rising from $13 a month to $8,000. When you connect a metered external API to a public feature, make sure there is a layer that holds even when your own code is wrong. What happens when a key leaks is covered in a stolen key, a bill and a suspended account and an API key leaked from AI-written code.
Expensive operations (search, export, file processing)
Put explicit ceilings on execution time, memory, upload size and records returned per page. Anywhere without a ceiling is an entrance for exhausting your resources in a single request. Limit memory, CPU and process count at the container or serverless layer too, so that a runaway application is stopped from outside.
Know when the ceiling is being hit
A limit you cannot observe is half a control. If nobody can see that the ceiling is being reached, you will notice neither an attack in progress nor legitimate users being shut out. Record how often limits trigger and alert on a spike. This is the "able to notice something is wrong" state from the organisational security baseline, applied here.
This site's view: a limit is not a wall, it is economics
Treat rate limiting as "a wall that blocks attacks" and you end up stuck on an unanswerable question: how many attempts is safe? We see it differently — this is economic design. Raise the attacker's cost per attempt (time, computation, CAPTCHA, a second factor) and lower the expected gain per attempt (odds of a hit, how far a hit gets them). You do not have to drive the success rate to zero; you have to make it not worth doing.
Which is why we favour delays and a second factor over harsh lockout. Money is the one case where that reasoning changes: the only reliable ceiling is the spending limit that lives outside your own code. Code is wrong sometimes — put a layer one step further out where being wrong still cannot produce an unbounded bill.
Sources (primary)
- NIST SP 800-63B, "Digital Identity Guidelines: Authentication and Lifecycle Management" §5.2.2 Rate Limiting (Throttling) — pages.nist.gov (the SHALL limiting consecutive failures to no more than 100; CAPTCHA, growing waits, IP allow-lists and risk-based techniques; the SHOULD on disregarding previous failures from the same IP after success)
- OWASP API Security Top 10 2023, "API4:2023 Unrestricted Resource Consumption" — owasp.org (the resources that need limits, the cost impact, and configuring third-party spending limits)
Read next
- Foundation: X-Forwarded-For spoofing and trusted proxies (the basis for "who you count")
- Design: password reset design flaws / choosing multi-factor authentication
- Cost: a stolen key, a bill and a suspended account / an API key leaked from AI-written code
FAQ
QHow many failed logins should lock an account — three? five?
NIST SP 800-63B requires that the verifier limit consecutive failed authentication attempts on a single account to no more than 100. Three or five is not a requirement of the standard. What the same document asks for instead, to avoid locking out legitimate users, is a CAPTCHA before authentication, a waiting period that grows as the account approaches its maximum allowance, an allow-list of IP addresses the subscriber has authenticated from before, and risk-based or adaptive techniques. Aggressive lockout also hands an attacker a way to freeze other people's accounts deliberately. Raise the cost of each attempt rather than lowering the count alone.
QIs limiting by IP address enough?
No. Behind a reverse proxy or CDN, the address your application sees may not be the real source: trust X-Forwarded-For unconditionally and an attacker becomes a different client just by rewriting a header, so you are counting nothing. And legitimate users share addresses through corporate NAT and mobile networks, so per-IP limits catch bystanders. Make an identifier tied to the actor — account, API key, established session — the primary axis, and treat IP as a supporting signal.
QHow do I stop confirmation-email bombing?
You need both a limit on who can trigger a send and a count of how many go to the same destination. If your form accepts somebody else's address, an attacker can use you to harass a third party and damage your domain's sending reputation at the same time. Combine a cooldown per destination address, a minimum interval between resends, and deletion of records that were never confirmed after a set time — which also reduces the unverified personal data you hold.
QWhat is the biggest danger when putting an AI API behind a public feature?
The bill. OWASP's API Security Top 10 lists, under API4:2023 Unrestricted Resource Consumption, the limits an API needs — execution timeouts, memory, upload size, operations per request — and includes spending limits for third-party providers alongside them. Its scenarios include a bill going from $13 a month to $8,000. Limiting request counts does not protect you from operations that are individually expensive. Configure the provider-side spending limit: it is the one ceiling that still holds when your own code is wrong.