// Exemplu clasic de middleware pentru rate limit cu Redis (fixed window)
async function rateLimiter(req, res, next) {
const ip = req.ip;
const key = `rate:${ip}`;
const limit = 100; // max 100 request-uri
const windowInSeconds = 60;
try {
const current = await redis.incr(key);
if (current === 1) {
await redis.expire(key, windowInSeconds);
}
if (current > limit) {
return res.status(429).json({ error: 'Prea multe cereri. Încearcă mai târziu.' });
}
next();
} catch (err) {
// Fail-open: dacă pică Redis, lăsăm request-ul să treacă în producție
console.error('Redis error in rate limiter:', err);
next();
}
}Am văzut prea des echipe care pun rate limiting direct în Express sau Fastify folosind memoria RAM a procesului și se miră că le crapă containerul la primul atac mai serios. Dacă ai mai multe instanțe în spatele unui load balancer, abordarea asta in-memory e complet inutilă fiindcă starea nu e partajată. Alegerea locului unde pui frâna îți salvează fie baza de date, fie bugetul de infrastructură.
Am trecut prin toate variantele astea în producție și fiecare vine cu bubele ei. Hai să le luăm la rând.
1. Nginx: Primul zid de apărare (Layer 7 pur)
Dacă vrei doar să nu-ți pice serverul când un băiat deștept dă cu un script de benchmark în endpoint-ul tău de login, Nginx e sfânt. E extrem de rapid pentru că blochează request-ul înainte ca acesta să atingă codul tău Node, Python sau Go.
Am avut un caz la un proiect de e-commerce cu vreo 12.000 de useri unici pe zi unde un bot ne scana rutele de căutare agresiv. Am pus o regulă simplă de limit_req_zone în Nginx și am redus încărcarea pe CPU a serverelor de aplicație cu 40% instant.
- De ce e mișto: Zero overhead pe aplicație, consumă extrem de puține resurse și e gata configurat în infrastructura clasică.
- De ce e nasol: E rigid. E greu să faci rate limiting dinamic în funcție de planul tarifar al userului (ex: Free are 10 req/min, Premium are 1000). Dacă ai nevoie de logică de business complexă, Nginx devine un coșmar de configurat cu module de Lua.
2. Redis: Standardul din industrie (App-level)
Dacă ai un API public și vrei să limitezi userii logați în funcție de API key sau ID-ul de sesiune, Redis e regele. Stochezi cheia ca un identificator unic asociat cu un counter care expiră (TTL).
Când lucram la un SaaS cu 8.000 de utilizatori activi, foloseam algoritmul de token bucket implementat peste Redis. Fiecare request consumă un jeton dintr-o găleată virtuală. E super precis și scalabil pe orizontală.
- Trade-off-ul sincer: Ai o conexiune în plus de gestionat în aplicație. Dacă Redis are latență mare sau pică, trebuie să decizi: lași toate request-urile să treacă (fail-open) sau blochezi tot (fail-closed)? Eu merg de obicei pe fail-open în producție. Securitatea nu trebuie să strice experiența userilor legitimi dacă infrastructura noastră are sughițuri temporare.
3. Upstash (Serverless Ratelimit): Calea modernă și leneșă
Dacă rulezi pe Vercel, Cloudflare Workers sau AWS Lambda, nu vrei să configurezi și să menții tu un cluster de Redis doar pentru limitarea cererilor. Aici intră în scenă Upstash. Ei oferă un SDK de rate limit bazat pe apeluri HTTP, optimizat pentru medii edge (serverless).
- De ce e mișto: Îl configurezi în 5 minute. Nu ai conexiuni TCP persistente care să îți blocheze funcțiile serverless.
- De ce e nasol: Fiecare verificare înseamnă un request HTTP către Upstash. Chiar dacă e pe edge și e rapid (sub 10-15ms), tot adaugă o mică latență la API-ul tău, comparat cu un Redis local în același VPC care răspunde în sub 1ms. La un volum uriaș de request-uri, te va costa mult mai mult decât un server Redis pe care îl rulezi singur.
Nginx câștigă când vrei protecție brută împotriva spamului la nivel de IP. Redis câștigă când ai o aplicație clasică și vrei control granular bazat pe utilizator. Upstash e perfect pentru serverless și prototipuri rapide unde timpul tău de development e mai prețios decât latența de câteva milisecunde.
Voi unde puneți frâna? Direct în proxy sau lăsați aplicația să decidă?