TL;DR
Rate limiting restricts how many requests a user can make in a time period. Without it, attackers can brute force passwords, enumerate users, scrape your data, or overwhelm your servers. Add rate limits to login endpoints, API routes, and any expensive operations.
Why Rate Limiting Matters
Vibe-coded apps often skip rate limiting because it seems like an optimization, not a security feature. But without limits, your app is vulnerable to automated attacks that would be impractical with proper throttling.
What Attackers Can Do
- Brute force: Try thousands of passwords per second
- Credential stuffing: Test leaked password databases
- User enumeration: Discover valid usernames
- Data scraping: Extract your entire database
- DoS: Exhaust your server resources or API quotas
Recommended Rate Limits
| Endpoint | Limit | Why |
|---|---|---|
| Login | 5 per minute per IP | Prevents brute force |
| Password reset | 3 per hour per email | Prevents enumeration |
| API (authenticated) | 100 per minute per user | Fair usage |
| API (public) | 20 per minute per IP | Prevents scraping |
Implementation Example
import rateLimit from 'express-rate-limit';
// General API limit
const apiLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 100,
message: { error: 'Too many requests' }
});
// Strict limit for login
const loginLimiter = rateLimit({
windowMs: 60 * 1000,
max: 5,
message: { error: 'Too many login attempts' }
});
app.use('/api/', apiLimiter);
app.post('/api/login', loginLimiter, handleLogin);
Should I rate limit by IP or by user?
Both. Use IP-based limits for unauthenticated endpoints and user-based limits for authenticated ones. Consider that attackers can use many IPs, so also implement account-level lockouts.
What about serverless functions?
Serverless platforms often have built-in rate limiting. Check your provider's docs. For Vercel, use their edge middleware or a service like Upstash Redis for distributed rate limiting.