Cap Abuse, Protect Real Users
Rate Limiting
express-rate-limit caps how many requests a client can make. Lock down login endpoints especially.
What you'll learn
- Apply a global rate limit
- Tighter limits on auth endpoints
- Use Redis for multi-instance setups
Rate limiting is the cheapest defense against abuse. Apply it before expensive operations (DB lookups, password hashing, AI calls).
Install
npm install express-rate-limit Global Limit
import rateLimit from "express-rate-limit";
const apiLimiter = rateLimit({
windowMs: 60 * 1000, // 1 min
max: 100, // 100 req/min per IP
standardHeaders: true,
legacyHeaders: false,
});
app.use(apiLimiter); Clients over the limit → automatic 429 response with helpful
headers (RateLimit-Limit, RateLimit-Remaining, Retry-After).
Stricter on Auth
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 min
max: 5, // 5 attempts
message: "Too many login attempts. Try again in 15 minutes.",
});
app.post("/auth/login", authLimiter, loginHandler);
app.post("/auth/reset", authLimiter, resetHandler); 5 attempts per 15 minutes per IP. Brute force becomes impractical.
Per-User vs Per-IP
By default, the limiter keys by IP. For per-user limits:
const userLimiter = rateLimit({
windowMs: 60_000,
max: 1000,
keyGenerator: (req) => req.user?.id ?? req.ip,
}); Authenticated users get a higher limit; anonymous users fall back to IP-based.
Multiple Instances → Redis
When you run multiple Node processes (cluster, multiple containers), in-memory counters are wrong — each process has its own.
npm install rate-limit-redis ioredis import { RedisStore } from "rate-limit-redis";
import { Redis } from "ioredis";
const redis = new Redis(process.env.REDIS_URL);
const apiLimiter = rateLimit({
store: new RedisStore({ sendCommand: (...args) => redis.call(...args) }),
windowMs: 60_000,
max: 100,
}); Now all instances share the same counter.
trust proxy
If you’re behind a proxy (Nginx, Cloudflare), Express sees the
proxy’s IP as req.ip — every request looks like the same client.
Bad for rate limits.
app.set("trust proxy", 1); Tells Express to honor X-Forwarded-For. Only do this when you
really are behind a known proxy — otherwise clients can spoof
their IP.
Beyond Express
For sophisticated abuse, the application layer is too late.
- Cloudflare — bot detection, rate limit at the edge
- AWS WAF — managed rule sets
- Fastly Edge — programmable edge rate limits
But express-rate-limit is the baseline. Add it on day one.