Rate Limiting & Abuse Protection
Protect APIs from abuse with intelligent rate limiting.
Rate Limit Strategies
Fixed Window: 100 requests per hour Sliding Window: More accurate, prevents bursts Token Bucket: Allow bursts up to limit Leaky Bucket: Smooth request rate
Implementation (Express) import rateLimit from "express-rate-limit";
// Global rate limit const globalLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // 100 requests per window message: "Too many requests, please try again later", standardHeaders: true, // Return rate limit info in headers legacyHeaders: false, });
// Stricter limit for auth endpoints const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5, // Only 5 attempts skipSuccessfulRequests: true, // Don't count successful logins });
app.use("/api/", globalLimiter); app.use("/api/auth/login", authLimiter);
Redis-based Rate Limiting import Redis from "ioredis";
const redis = new Redis();
export const checkRateLimit = async ( key: string, max: number, window: number ): Promise<{ allowed: boolean; remaining: number }> => { const now = Date.now(); const windowStart = now - window;
await redis
.multi()
.zremrangebyscore(key, 0, windowStart)
.zadd(key, now, ${now})
.zcard(key)
.expire(key, Math.ceil(window / 1000))
.exec();
const count = await redis.zcard(key);
return { allowed: count <= max, remaining: Math.max(0, max - count), }; };
Per-User Rate Limiting export const userRateLimit = (max: number, window: number) => { return async (req, res, next) => { if (!req.user) return next();
const key = `rate_limit:user:${req.user.id}`;
const result = await checkRateLimit(key, max, window);
res.setHeader("X-RateLimit-Limit", max);
res.setHeader("X-RateLimit-Remaining", result.remaining);
if (!result.allowed) {
return res.status(429).json({
error: "Rate limit exceeded",
retryAfter: window / 1000,
});
}
next();
}; };
IP-based Protection
// Block suspicious IPs
const ipBlocklist = new Set
export const checkIPReputation = async (ip: string): Promise
// Check against threat intelligence API const reputation = await checkThreatIntel(ip); if (reputation.isMalicious) { ipBlocklist.add(ip); return false; }
return true; };
Response Headers X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1640000000 Retry-After: 3600
Best Practices Different limits for different endpoints Lower limits for expensive operations Skip rate limit for internal services Return helpful error messages Log rate limit violations Monitor for abuse patterns Allowlist trusted IPs Output Checklist Rate limiter middleware Per-route policies User-based limiting IP-based limiting Rate limit headers Safe error responses Observability/logging Bypass for internal services