Rate limiting checklist for SaaS auth routes: is your login page a sitting duck?
Nobody asks an AI builder for rate limiting, so it usually isn't there. Here's what an unlimited login, signup, or reset endpoint actually costs you — and it isn't always "got hacked."
What no rate limit on login, signup, or reset looks like in practice
- Bot signups. An open signup endpoint with no limit gets found and hit by generic signup bots within days of going live — not because anyone is targeting you specifically, but because the internet is full of scripts that try every new site they find.
- Credential stuffing. A login endpoint with no limit lets an attacker try thousands of leaked email/password pairs from unrelated breaches against your app, looking for the small percentage of people who reused a password.
- Surprise OTP/SMS bills. If your reset or signup flow sends a code by SMS or email and nothing limits how often it can be triggered, an attacker (or a bot) can rack up a real bill in your Twilio or email provider account just by repeatedly requesting codes for addresses or numbers they don’t own.
A quick self-check, right now
Open a terminal and hit your own login endpoint a handful of times in a row:
for i in $(seq 1 20); do
curl -s -o /dev/null -w "%{http_code}\n" \
-X POST https://yourapp.com/api/login \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"wrong"}'
doneIf all 20 come back 200 or 401 with no slowdown and no blocked response, there’s nothing between that endpoint and however many requests someone wants to send it. Run the same shape of test against your signup and password-reset endpoints too — they’re just as often unlimited.
What “good” rate limiting looks like for a small SaaS
You don’t need enterprise-grade infrastructure for this. A reasonable baseline: block or heavily slow an IP after roughly 5–10 failed login attempts in a short window, cap signups and password-reset requests per IP and per email address, and return a normal-looking error rather than a response that tells an attacker exactly why they were blocked. Most hosting platforms and edge functions have a built-in or one-line way to do this — the gap is almost always that nobody asked for it, not that it’s hard.
C1: verified as actually working, not just configured
See a C1 finding, with the request count that proved it.
The sample report is a real scan of a deliberately vulnerable app — nine findings, the proof behind each one, and what they would cost.