Immaginate un'autostrada digitale senza controlli di velocità. Senza una corretta limitazione del tasso di richieste (rate limiting), un singolo utente o, peggio ancora, un bot malevolo, potrebbe inondare le vostre API con migliaia di richieste al secondo. Questo non solo sovraccaricherebbe i vostri server, compromettendo le prestazioni per tutti gli utenti, ma farebbe anche lievitare i costi dell'infrastruttura. Il rate limiting è la vostra barriera protettiva essenziale: un meccanismo che regola il numero di richieste che un client può effettuare in un determinato intervallo di tempo, garantendo stabilità e equità.
Perché Implementare il Rate Limiting?
- Prevenire Abusi: Blocca tentativi di accesso a forza bruta, spamming e scraping di dati.
- Proteggere le Risorse: Impedisce che un singolo utente monopolizzi la capacità del server e le risorse computazionali.
- Garantire Equità: Assicura a tutti gli utenti un accesso bilanciato e imparziale alle tue API.
- Controllare i Costi: Limita l'esecuzione di operazioni costose, come inferenze AI complesse o query intensive sul database.
- Conformità SLA: Permette di applicare i livelli di utilizzo previsti nei piani tariffari delle tue API, rispettando gli accordi di servizio.
Algoritmi di Rate Limiting
1. Finestra Fissa (Fixed Window)
Questo algoritmo, semplice e diretto, divide il tempo in intervalli prefissati, come un minuto o un'ora. Ogni richiesta all'API viene conteggiata all'interno della finestra temporale corrente. Una volta che il numero di richieste supera la soglia consentita per quella finestra, tutte le richieste successive vengono respinte fino all'inizio della finestra successiva. È facile da implementare ma presenta un difetto significativo.
Finestra: 1 minuto | Limite: 100 richieste 12:00:00 - 12:00:59 → Richiesta 1...100 ✅ | Richiesta 101 ❌ 12:01:00 - 12:01:59 → Contatore si azzera → Richiesta 1...100 ✅ ⚠️ Problema intrinseco: Un utente può inviare 100 richieste alle 12:00:59 e altre 100 alle 12:01:00 — ovvero 200 richieste in soli 2 secondi! Questo "effetto burst" ai bordi della finestra può sovraccaricare il sistema.
2. Finestra Scorrevole a Log (Sliding Window Log)
Questo metodo è considerato il più preciso. Per ogni client, memorizza un timestamp di ogni singola richiesta effettuata. Quando una nuova richiesta arriva, il sistema elimina tutti i timestamp più vecchi dell'intervallo di tempo definito (ad esempio, gli ultimi 60 secondi) e conta le richieste rimanenti. Se il conteggio supera il limite, la richiesta viene bloccata. Offre un'accuratezza impeccabile contro gli "effetti burst", ma è estremamente intensivo in termini di memoria, soprattutto con un elevato volume di richieste e finestre temporali lunghe.
3. Contatore a Finestra Scorrevole (Sliding Window Counter)
Una soluzione intermedia che cerca di bilanciare accuratezza e consumo di memoria. Combina i principi della finestra fissa e della finestra scorrevole. Utilizza una media ponderata dei conteggi della finestra corrente e della finestra precedente. Per calcolare il numero di richieste in un dato momento, prende il conteggio delle richieste della finestra fissa corrente e aggiunge una frazione del conteggio della finestra fissa precedente, proporzionale a quanto della finestra precedente rientra nell'intervallo scorrevole. Questo riduce l'effetto "burst" della finestra fissa senza la pesantezza del log completo.
4. Bucket di Token (Token Bucket) (Consigliato)
L'algoritmo Token Bucket è spesso preferito per la sua flessibilità e capacità di gestire i 'burst' di traffico in modo controllato. Immaginate un secchio virtuale (bucket) che contiene un certo numero di 'token'. Ogni richiesta API consuma un token dal secchio. I token vengono aggiunti al secchio a un ritmo costante. Se un client tenta di effettuare una richiesta e il secchio è vuoto, la richiesta viene rifiutata. Questo approccio permette ai client di effettuare brevi raffiche di richieste (fino alla capacità del secchio) pur mantenendo un tasso medio di richieste controllato nel tempo. È eccellente per prevenire picchi improvvisi che potrebbero altrimenti sovraccaricare il sistema.
Capacità del bucket: 10 token
Tasso di ricarica: 1 token/secondo
Secondo 0: Il bucket contiene 10 token
→ L'utente invia 5 richieste → 5 token consumati → 5 rimanenti
Secondo 1: 1 token aggiunto → 6 token
→ L'utente invia 1 richiesta → 5 rimanenti
Secondo 5: 4 token aggiunti (arrivando a 9)
→ L'utente invia una raffica di 9 richieste → 0 rimanenti
Secondo 6: 1 token aggiunto → 1 token
→ L'utente può effettuare 1 richiesta
Implementazione con Redis
import Redis from 'ioredis';
const redis = new Redis();
async function rateLimiter(req, res, next) {
const key = `rate:${req.ip}`;
const limit = 100; // requests
const windowMs = 60 * 1000; // per minute
const now = Date.now();
const windowStart = now - windowMs;
// Atomic Redis operations
const pipeline = redis.pipeline();
pipeline.zremrangebyscore(key, 0, windowStart); // Remove old entries
pipeline.zadd(key, now, `${now}-${Math.random()}`); // Add current request
pipeline.zcard(key); // Count requests in window
pipeline.expire(key, 60); // Auto-cleanup
const results = await pipeline.exec();
const requestCount = results[2][1];
// Set rate limit headers
res.set({
'X-RateLimit-Limit': limit,
'X-RateLimit-Remaining': Math.max(0, limit - requestCount),
'X-RateLimit-Reset': Math.ceil((now + windowMs) / 1000),
});
if (requestCount > limit) {
res.set('Retry-After', '60');
return res.status(429).json({
error: 'Too Many Requests',
message: 'Rate limit exceeded. Please try again later.',
retryAfter: 60,
});
}
next();
}
app.use('/api/', rateLimiter);
Header per il Rate Limiting
Per una gestione ottimale del rate limiting e per permettere ai client di adattarsi autonomamente, è fondamentale utilizzare header HTTP standard che comunichino lo stato attuale della limitazione.
HTTP/1.1 200 OK
X-RateLimit-Limit: 100 // Max requests per window
X-RateLimit-Remaining: 73 // Requests remaining
X-RateLimit-Reset: 1720396800 // Unix timestamp when window resets
HTTP/1.1 429 Too Many Requests
Retry-After: 60 // Seconds until client can retry
Content-Type: application/json
{
"error": "Too Many Requests",
"retryAfter": 60
}
Migliori Pratiche
- Limita per chiave API per gli utenti autenticati e **per IP** per quelli anonimi.
- Applica limiti diversi per endpoint specifici: gli endpoint di login, ad esempio, richiedono restrizioni più severe rispetto a quelli di sola lettura.
- Restituisci risposte 429 informative (Too Many Requests) includendo sempre l'header `Retry-After`.
- Includi gli header di rate limiting in ogni risposta, così i client possono autoregolarsi e prevenire ulteriori blocchi.
- Utilizza Redis per implementare un rate limiting distribuito e sincronizzato su più istanze del server.
- Valuta limiti differenziati per tier: ad esempio, 100 richieste/minuto per utenti gratuiti e 1000/minuto per utenti premium.
- Non applicare il rate limiting agli endpoint di health check: i servizi di monitoraggio necessitano di accesso illimitato per verificare lo stato del sistema.
Prova i Nostri Strumenti Gratuiti per Sviluppatori
Formatta e convalida le tue risposte API istantaneamente, migliorando il tuo flusso di lavoro.