What Is Rate Limiting? Protecting Your App From Too Many Requests
Rate limiting caps how many requests someone can make in a period. Why every public app needs it — for login forms, sign-ups, AI features, and APIs — how it works, what a 429 response means, where to add limits, and what to do when you hit someone else's.
Your login form will be attacked by bots trying thousands of passwords. Your sign-up form will be spammed. Your AI feature will be used by someone as a free AI service at your expense. None of this is personal — automated scripts do it to every public app. Rate limiting is one of the simplest defences against all of it.
What it is
A rate limit caps how many requests someone can make in a period of time. For example:
- 5 login attempts per minute per email address.
- 3 sign-ups per hour per IP address.
- 100 API requests per minute per user.
- 50 AI messages per day on the free plan.
When someone exceeds the limit, the server refuses — politely — until the period resets.
What it protects against
- Password guessing. Without a limit, a bot can try thousands of passwords per minute against one account. With 5 per minute, the attack becomes hopeless. (Password Hashing Explained.)
- Spam and fake accounts. Limits on sign-ups, contact forms, and comments.
- Cost attacks. If each request costs you money — AI calls, emails, SMS — a limit caps your worst day. (How to Stop Bots From Running Up Your AI App's Bill.)
- Scraping. Limits slow down bulk copying of your data.
- Accidental overload. A bug in someone's script — or your own frontend stuck in a loop — can't take the whole app down.
What a rate-limited response looks like
The standard response is HTTP status 429 Too Many Requests, often with a header saying when to try again:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Your app should show a friendly message — "Too many attempts. Try again in 30 seconds." — not a generic error. (HTTP Status Codes Explained.)
Who to limit: the "key"
A rate limit counts requests per something:
- Per IP address — works for logged-out visitors. Imperfect: many people can share one IP (an office, a mobile network), and attackers can use many.
- Per user account — fairer and more precise for logged-in features.
- Per email or username being targeted — for login forms, so an attacker can't spread guesses across many IPs against one account.
- Per API key — for APIs used by other developers.
Good setups combine them: per IP and per account on login, for example.
Where to add rate limits
- Login, sign-up, and password reset. Always.
- Anything that costs money per request — AI calls, SMS, emails.
- Anything that writes data — forms, comments, uploads.
- Expensive operations — search, exports, reports.
- Your whole API, with a generous general limit as a safety net.
How to add them
The limit must be enforced on the server. A limit in front-end JavaScript is trivially bypassed — attackers don't use your front end.
Options, from least to most work:
- Your platform or login service may have it built in. Authentication services typically rate-limit login and sign-up for you. Check the settings.
- A CDN or firewall in front of your app can limit by IP. (What Is a CDN?.)
- Middleware in your app. Most frameworks have a rate-limiting library — for Express, for example,
express-rate-limit:
import rateLimit from "express-rate-limit"
app.use("/api/login", rateLimit({
windowMs: 60 * 1000, // 1 minute
limit: 5, // 5 attempts per window
}))
One important detail: by default, many libraries keep counts in the server's memory. That resets whenever the app restarts, and doesn't work if you run more than one copy. For real protection, store counts in a shared place like Redis or your database. (Redis: When You Actually Need It and Implementing Rate Limiting.)
Choosing numbers
Start generous for normal use and strict for sensitive actions. A real person rarely submits a login form more than a few times a minute, or signs up more than once an hour. Watch your logs after launch and adjust. Limits so tight that real users hit them are a bug too.
When you hit a rate limit
Your app calls other services — AI providers, GitHub, Stripe — which have their own limits. When you get a 429:
- Wait and retry, respecting any
Retry-Afterheader, with increasing delays between tries. - Don't retry instantly in a loop — that makes it worse.
- Reduce calls: cache results, batch requests, or move work to a background job. (What Is Caching?.)
EasySpawn runs your backend as a real server, so rate limits live on the server where they belong — with Redis available on the Team plan to share counts across restarts. See pricing or join the waitlist.
Related: A Security Checklist for Vibe-Coded Apps · Add Login to an AI-Built App · What Is an API?
Keep reading
What Is XSS? Cross-Site Scripting Explained for Beginners
Cross-site scripting lets an attacker run their JavaScript in your users' browsers — stealing sessions, changing pages, acting as the user. How XSS works, the three types, why React mostly protects you, the escape hatches that don't, and the defences that matter.
What Is "the Cloud"? Cloud Computing for Beginners
The cloud is other people's computers, rented by the minute. What cloud computing actually is, the difference between IaaS, PaaS, and SaaS, what AWS, Google Cloud, and Azure sell, how cloud billing works, and how much of it a first app really needs.