Rate limiting and account lockout: balancing security and access
A client once asked for accounts to lock after three wrong passwords, permanently, until someone at the office unlocked them. Within a week, their dealer portal had dozens of locked accounts every Monday morning, some from forgetful dealers and some from a bot cycling through usernames. The security intention was right. The design was wrong. Login rate limiting is about finding the point where attackers are slowed to a crawl and genuine people barely notice.
This article is for developers and technically minded owners deciding how their limits should behave.
Two separate goals, often confused
It helps to separate what you are trying to achieve:
- Slow down guessing so that trying many passwords becomes too time-consuming to be worthwhile.
- Keep the service available so that legitimate users, including ones who mistype, can still sign in.
A hard lockout achieves the first goal while undermining the second. Most well-designed systems use throttling and delays as the main tool and reserve true lockouts for narrow cases.
Per-IP limits: useful, but easy to sidestep
Limiting how many login attempts a single IP address can make in a window is simple and cheap. It stops a lone script hammering from one server.
Where per-IP limits fall short
- Distributed attacks rotate through many addresses, each staying under the limit.
- Shared addresses are common in India. Mobile carriers use carrier-grade NAT, so many unrelated customers can appear from one IP. Offices, colleges and cyber cafés also share addresses. A strict per-IP limit can block a whole group of innocent users.
- Proxies and CDNs can make every request look like it comes from the proxy unless your application reads the forwarded client IP correctly, and only from trusted proxies.
Use per-IP limits with generous thresholds as a first filter, not the whole solution.
Per-account limits: targeted, but abusable
Counting failed attempts against the account being targeted catches guessing even when it is spread across many IPs. It is the natural way to protect a specific user.
The risk is the one from our opening example: if failures lock the account, an attacker can lock any account they know the username for. For a store during a sale, or staff logins before a deadline, that is a denial-of-service against your own customers.
Combining both dimensions
A common pattern tracks failures on the pair of account and IP, plus looser counters on account alone and IP alone. A user retrying from their usual phone is treated gently, while one account being tried from many sources or one source trying many accounts triggers stronger measures.
Progressive delays instead of hard stops
Rather than allowing five attempts and then blocking, increase the waiting time as failures build up. An illustrative schedule might look like this:
- First few failures: no delay, since typos are normal.
- Next failures: a short wait of a few seconds before another attempt is accepted.
- Continued failures: the wait doubles each time, up to a ceiling such as 15 minutes.
- A successful login or a completed password reset resets the counter.
The exact numbers are yours to tune, but the principle holds: an honest user who forgot a password is slowed briefly and pointed towards “Forgot password”, while an attacker wanting thousands of guesses faces days or weeks per account.
Adding a challenge before a delay
Instead of making someone wait, you can require a CAPTCHA or similar challenge after several failures. Humans pass it and continue; simple bots stall. Combine with delays for persistent failures.
When a true lockout is justified
Hard lockouts still have a place:
- Highly privileged accounts, such as server or payment administrators, where the cost of a wrong guess succeeding is severe and the users are few and reachable.
- Short one-time codes and PINs, which have a small number of possibilities and must be invalidated after a handful of wrong entries.
- Situations where monitoring shows a confirmed attack on a specific account.
Even then, make the lockout time-bound or give a self-service unlock path, and always notify the account holder.
Unlocking safely
How users get back in matters as much as how they get locked out. An unlock route that skips verification is simply another login.
- Automatic expiry: the restriction lifts after the delay passes. No action needed.
- Self-service unlock: a link sent to the verified email or an OTP to the registered mobile lets the user clear the restriction, ideally combined with a password reset.
- Assisted unlock: for admin accounts, a second administrator verifies identity through a separate channel before clearing the lock. Record who approved it.
Whatever route you use, notify the account holder that there were repeated failed attempts, including approximate time and location if known. That message often alerts people to a reused password they should change elsewhere.
What to tell users on screen
Messages should help real users without advertising your thresholds or confirming which accounts exist. Something like “Too many attempts. Please wait a few minutes or reset your password” works for both registered and unregistered usernames. Return an HTTP 429 (Too Many Requests) status from APIs, optionally with a Retry-After header so well-behaved clients know when to try again.
Where to enforce limits
- At the edge: a CDN or web application firewall rate rule absorbs floods before they reach your server.
- In the application: framework features, such as Laravel’s built-in rate limiter, know which account is targeted and can apply account-aware logic.
- On every route that checks a secret: web login, API tokens, mobile app login, OTP verification, password reset and 2FA code entry.
Tuning and reviewing your limits
Launch with moderate settings, then review logs after a couple of weeks. Look at how many real users hit the delays, whether support tickets about lockouts increased, and whether attack traffic was slowed. Adjust thresholds rather than guessing forever. If you run a custom portal, our software development team can implement account-aware throttling, and you can ask an expert to review your current settings.
Frequently asked questions
How many failed login attempts should be allowed?
There is no universal number. Many sites allow a handful of free attempts, then add increasing delays. NIST guidance requires limiting consecutive failures to no more than 100 per account, which is an upper bound rather than a target.
Can account lockout be used against my customers?
Yes. If failures lock accounts outright, anyone who knows a username can deliberately lock it. Time-limited delays and self-service unlocking reduce that risk.
Is IP-based rate limiting enough on its own?
No. Distributed attacks use many IPs, and shared mobile or office addresses mean strict IP limits can block genuine users. Combine it with account-aware limits.
What HTTP status code should a rate-limited request return?
Use 429 Too Many Requests, optionally with a Retry-After header indicating when the client may try again.
We can do this for you
See what a package covers and what it costs, or ask us about your own project.

