Exposing unrestricted API endpoints without rate limiting is an open invitation to automated abuse. Without rate limits, adversaries deploy automated multi-threaded scripts to brute force user passwords, scrape proprietary catalog databases, exhaust third-party SMS billing credits, or launch application-layer denial-of-service floods. Rate Limiting and Throttling enforces consumption boundaries, ensuring service availability and stability.
| Algorithm | How It Works | Pros & Trade-offs |
|---|---|---|
| Token Bucket | Tokens are added to a bucket at a fixed constant rate. Each API request consumes one token. If the bucket is empty, requests are rejected (HTTP 429). | Allows short bursts of traffic up to the bucket capacity while maintaining a stable long-term average. |
| Leaky Bucket | Requests enter a queue (bucket) and are processed at a smooth, constant output rate (like water leaking from a hole). | Smooths out traffic spikes; but drops requests if the queue overflows during sudden bursts. |
| Sliding Window Log | Records timestamps of each request in a sorted set (e.g. Redis). Sums requests in the last 60 seconds dynamically. | Most accurate! Eliminates boundary burst vulnerabilities; requires slightly more memory. |
When a client exceeds permitted limits, the server should respond with HTTP 429 Too Many Requests and emit informative RFC 6585 headers:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1799880060
Content-Type: application/json
{ "error": "Rate limit exceeded. Please wait 60 seconds before retrying." }
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const { createClient } = require('redis');
const redisClient = createClient({ url: 'redis://localhost:6379' });
redisClient.connect();
// Strict limiter for authentication login route (5 attempts per 15 minutes)
const loginLimiter = rateLimit({
store: new RedisStore({
sendCommand: (...args) => redisClient.sendCommand(args),
}),
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // Limit each IP to 5 requests per windowMs
standardHeaders: true,
legacyHeaders: false,
message: { error: 'Too many login attempts. Account temporarily throttled for 15 minutes.' }
});
app.post('/api/auth/login', loginLimiter, handleLogin);
Broaden your defensive and offensive security knowledge across our curriculum:
Defending against application-layer HTTP floods with algorithmic rate limiters.
Throttling failed authentication attempts to halt credential stuffing.
Remediating API4:2023 Unrestricted Resource Consumption with token buckets.
Production code configuring Redis-backed rate limiting in Node.js applications.