Rate Limiting em APIs: Algoritmos e Implementações
Sua API está sob ataque. Não por hackers sofisticados, mas por um script simples que martela seus endpoints milhares de vezes por segundo, consumindo recursos e degradando o serviço para usuários legítimos. Sem rate limiting, um único cliente com comportamento inadequado pode derrubar toda a sua aplicação. Este guia explica os algoritmos principais e mostra implementações práticas em Node.js e Nginx para proteger sua API.
Por que o Rate Limiting é Importante
O rate limiting controla quantas requisições um cliente pode fazer em uma determinada janela de tempo. É sua primeira linha de defesa contra:
- Ataques de força bruta em endpoints de login ou chave de API
- Negacão de Serviço (DoS) por requisições excessivas
- Esgotamento de recursos por operações custosas como consultas a banco de dados ou processamento de arquivos
- Abuso de camadas gratuitas por scrapers ou bots
Além da segurança, o rate limiting garante uso justo e ajuda a aplicar regras de negócio, como planos de preços escalonados.
Algoritmos de Rate Limiting
Quatro algoritmos dominam o rate limiting em APIs. Cada um tem trade-offs em precisão, uso de memória e tratamento de picos.
1. Fixed Window
Conta requisições em intervalos de tempo fixos (ex.: 100 requisições por minuto). Quando a janela reinicia, o contador é zerado.
- Prós: Simples, baixo uso de memória (um contador por cliente).
- Contras: Picos nos limites da janela. Um cliente pode enviar 100 requisições às 12:00:59 e outras 100 às 12:01:00, efetivamente 200 requisições em dois segundos.
2. Sliding Window
Registra os timestamps de cada requisição e conta quantas caem nos últimos N segundos. Isso suaviza os picos.
- Prós: Preciso, sem picos nos limites.
- Contras: Maior uso de memória (armazenar timestamps) ou usar um contador de janela deslizante com média ponderada.
3. Token Bucket
Um balde contém tokens. Tokens são adicionados a uma taxa fixa. Cada requisição consome um token. Se o balde estiver vazio, a requisição é negada.
- Prós: Permite picos até o tamanho do balde, depois impõe a taxa média.
- Contras: Requer armazenar a contagem de tokens e o último horário de reabastecimento por cliente.
4. Leaky Bucket
Requisições entram em uma fila (balde) e são processadas a uma taxa constante. Se a fila estiver cheia, as requisições são descartadas.
- Prós: Suaviza o tráfego para uma taxa constante, ideal para proteger serviços downstream.
- Contras: Adiciona latência; não adequado para APIs em tempo real.
| Algoritmo | Tratamento de Picos | Memória | Caso de Uso |
|---|---|---|---|
| Fixed Window | Ruim | Baixa | APIs simples |
| Sliding Window | Bom | Média | Propósito geral |
| Token Bucket | Excelente | Média | APIs com tolerância a picos |
| Leaky Bucket | Nenhum | Média | Suavização de tráfego |
Implementando Rate Limiting em Node.js
Vamos implementar um rate limiter de token bucket usando Express e Redis para estado distribuído. Redis é essencial quando você tem múltiplas instâncias de servidor.
Passo 1: Instalar dependências
npm install express redis
Passo 2: Criar o middleware de rate limiter
const redis = require('redis');
const client = redis.createClient();
async function tokenBucketLimiter(req, res, next) {
const key = `rate_limit:${req.ip}`;
const capacity = 10; // max tokens
const refillRate = 1; // tokens per second
const now = Date.now();
const data = await client.hGetAll(key);
let tokens = data.tokens ? parseFloat(data.tokens) : capacity;
let lastRefill = data.lastRefill ? parseInt(data.lastRefill) : now;
// Refill tokens based on elapsed time
const elapsed = (now - lastRefill) / 1000;
tokens = Math.min(capacity, tokens + elapsed * refillRate);
if (tokens < 1) {
return res.status(429).json({ error: 'Too many requests' });
}
tokens -= 1;
await client.hSet(key, {
tokens: tokens.toString(),
lastRefill: now.toString()
});
await client.expire(key, 60); // auto-cleanup
next();
}
app.use(tokenBucketLimiter);
Este middleware verifica e atualiza a contagem de tokens atomicamente. Para produção, use transações Redis ou scripts Lua para evitar condições de corrida.
Implementando Rate Limiting no Nginx
O Nginx oferece rate limiting integrado com o módulo limit_req. Ele usa um algoritmo de leaky bucket.
Passo 1: Definir uma zona de rate limit
No bloco http do nginx.conf:
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
Isso cria uma zona de 10MB chamada api que permite 10 requisições por segundo por IP.
Passo 2: Aplicar o limite
No seu bloco location:
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend;
}
burst=20 permite picos curtos de até 20 requisições. nodelay processa as requisições do pico imediatamente em vez de enfileirá-las.
Boas Práticas para Rate Limiting em APIs
- Retorne códigos de status adequados: Use
429 Too Many Requestse inclua o cabeçalhoRetry-After. - Identifique clientes corretamente: Use chaves de API ou IDs de usuário em vez de endereços IP quando possível, pois IPs podem ser compartilhados (NAT) ou falsificados.
- Distribua o estado: Use Redis ou um armazenamento similar para implantações com múltiplas instâncias.
- Registre e monitore: Acompanhe as ocorrências de rate limit para detectar ataques e ajustar limites. Ferramentas como o Nginx Log Analyzer podem ajudar a analisar respostas 429 e identificar IPs abusivos.
- Comunique os limites: Documente os limites de taxa na documentação da sua API e inclua cabeçalhos como
X-RateLimit-Limit,X-RateLimit-Remaining.
FAQ
Qual é a diferença entre rate limiting e throttling?
O rate limiting bloqueia requisições além de um limite, enquanto o throttling as desacelera (por exemplo, enfileirando ou atrasando). O rate limiting é binário; o throttling é gradual.
Qual algoritmo de rate limiting devo usar?
Para a maioria das APIs, o token bucket oferece um bom equilíbrio: permite picos mas impõe uma taxa média. Se você precisa de suavização estrita, use leaky bucket. Para simplicidade, fixed window funciona para APIs de baixo tráfego.
Como lidar com rate limiting para usuários autenticados vs. anônimos?
Aplique limites mais rígidos para usuários anônimos (ex.: por IP) e limites mais generosos para usuários autenticados (ex.: por chave de API). Você também pode implementar limites escalonados com base em planos de assinatura.
Pronto para analisar o tráfego da sua API? Use nosso Nginx Log Analyzer para analisar logs, identificar violações de rate limit e otimizar seus limites.