Limitação de taxa protege sua API contra abusos, evita sobrecarga acidental e garante uso justo entre clientes. Sem ele, um único cliente (ou invasor) pode sobrecarregar seu servidor. Este guia implementa limitação de taxa robusta em Node.js, desde o nível simples até o nível de produção.
📋 Table of Contents
- Por que limite de taxa?
- Os principais algoritmos
- Simples: limite de taxa expressa
- Produção: Limitação de taxa apoiada por Redis
- Balde de token personalizado com Redis
- Limitação por usuário (não apenas por IP)
- Configurando cabeçalhos de resposta adequados
- Melhores Práticas
- Perguntas Frequentes
- Conclusão
Por que limite de taxa?
- Evite abusos: Pare de ataques de força bruta e raspagem
- Garanta justiça: Nenhum cliente monopoliza recursos
- Custos de controle: Limitar operações caras (chamadas de IA, consultas de banco de dados)
- Proteja a estabilidade: Evitar sobrecarga acidental ou maliciosa
Os principais algoritmos
| Algoritmo | Como funciona | Troca |
|---|---|---|
| Janela Fixa | N solicitações por janela de tempo fixa | Simples, mas permite bursts nas bordas das janelas |
| Janela Deslizante | N solicitações nos últimos X segundos (contínuos) | Mais suave, um pouco mais complexo |
| Balde de tokens | Os tokens são recarregados com o tempo; cada solicitação custa um | Permite rajadas controladas, flexíveis |
Simples: limite de taxa expressa
npm install express-rate-limit
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window per IP
standardHeaders: true, // return RateLimit-* headers
legacyHeaders: false,
message: { error: 'Too many requests, please try again later.' },
});
// Apply globally
app.use(limiter);
// Or stricter limits on specific routes
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // only 5 login attempts per 15 min
});
app.post('/login', authLimiter, loginHandler);
Produção: Limitação de taxa apoiada por Redis
Os limites na memória não funcionam em várias instâncias de servidor. O Redis compartilha limites em todas as instâncias:
npm install rate-limit-redis ioredis
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);
const limiter = rateLimit({
store: new RedisStore({
sendCommand: (...args) => redis.call(...args),
}),
windowMs: 15 * 60 * 1000,
max: 100,
});
app.use(limiter);
// Now all server instances share the same rate-limit counters
Balde de token personalizado com Redis
async function tokenBucket(key, maxTokens, refillRate) {
const now = Date.now();
const bucket = await redis.hgetall(`bucket:${key}`);
let tokens = parseFloat(bucket.tokens ?? maxTokens);
let lastRefill = parseInt(bucket.lastRefill ?? now);
// Refill tokens based on elapsed time
const elapsed = (now - lastRefill) / 1000;
tokens = Math.min(maxTokens, tokens + elapsed * refillRate);
if (tokens < 1) {
return { allowed: false, retryAfter: (1 - tokens) / refillRate };
}
tokens -= 1; // consume one token
await redis.hset(`bucket:${key}`, { tokens, lastRefill: now });
await redis.expire(`bucket:${key}`, 3600);
return { allowed: true, remaining: Math.floor(tokens) };
}
// Middleware
async function rateLimitMiddleware(req, res, next) {
const result = await tokenBucket(req.ip, 10, 1); // 10 tokens, 1/sec refill
if (!result.allowed) {
res.setHeader('Retry-After', Math.ceil(result.retryAfter));
return res.status(429).json({ error: 'Rate limit exceeded' });
}
res.setHeader('X-RateLimit-Remaining', result.remaining);
next();
}
Limitação por usuário (não apenas por IP)
// Rate limit by authenticated user ID instead of IP
const userLimiter = rateLimit({
windowMs: 60 * 1000,
max: 60,
keyGenerator: (req) => {
// Use user ID if authenticated, fall back to IP
return req.user?.id || req.ip;
},
store: new RedisStore({ sendCommand: (...args) => redis.call(...args) }),
});
// Tiered limits based on plan
function getLimitForUser(req) {
const plan = req.user?.plan || 'free';
return { free: 100, pro: 1000, enterprise: 10000 }[plan];
}
Configurando cabeçalhos de resposta adequados
// Standard rate-limit headers help clients back off gracefully
res.setHeader('RateLimit-Limit', limit);
res.setHeader('RateLimit-Remaining', remaining);
res.setHeader('RateLimit-Reset', resetTimestamp);
// On limit exceeded, always include Retry-After
res.status(429)
.setHeader('Retry-After', secondsUntilReset)
.json({ error: 'Too many requests' });
Melhores Práticas
- Use Redis para aplicativos de múltiplas instâncias — os limites na memória não são sincronizados entre servidores
- Limites mais rigorosos para terminais sensíveis — login, redefinição de senha e terminais de pagamento precisam de limites rígidos
- Retornar cabeçalhos claros — RateLimit-* e Retry-After ajudam os clientes a se comportarem bem
- Limite de taxa por usuário quando autenticado — mais justo do que apenas IP (IP partilhados, proxies)
- Considere limites escalonados — limites diferentes para usuários gratuitos e pagos
- Combine com outras defesas — a limitação de taxa é uma camada, não uma solução de segurança completa
Perguntas Frequentes
P: Qual algoritmo devo usar?
R: Balde de token para flexibilidade (permite rajadas controladas), janela deslizante para suavidade, janela fixa para simplicidade. Para a maioria das APIs, o padrão do limite de taxa expressa (janela fixa/deslizante) é adequado. O bucket de token é adequado para APIs onde rajadas ocasionais são aceitáveis.
P: Devo limitar a taxa por IP ou usuário?
R: Por ID de usuário quando autenticado (mais justo, lida com IPs compartilhados), recorrendo ao IP para solicitações anônimas. A limitação somente de IP pode afetar injustamente os usuários por trás de NATs compartilhados ou proxies corporativos.
P: Por que meu limite de taxa é redefinido inesperadamente?
R: Com a limitação na memória em múltiplas instâncias, cada servidor tem seu próprio contador — um cliente que atinge instâncias diferentes vê limites inconsistentes. Use o Redis para compartilhar o estado em todas as instâncias.
P: Qual código de status devo retornar?
A: 429 Solicitações demais, com umRetry-After cabeçalho informando ao cliente quanto tempo esperar. Este é o padrão e os clientes bem comportados o respeitam.
P: A limitação de taxa pode impedir ataques DDoS?
R: Ajuda contra o abuso da camada de aplicação, mas não é uma defesa completa contra DDoS. Para ataques em grande escala, use um CDN/WAF (Cloudflare, AWS Shield) na borda da rede, além da limitação da taxa de aplicação.
Conclusão
A limitação de taxa é essencial para proteger sua API Node.js contra abuso e sobrecarga. Comece comlimite de taxa expressa para casos simples e vá paraLimitação apoiada por Redis para aplicativos de produção de várias instâncias, limitando a sincronização entre servidores. Aplique limites mais rígidos para endpoints confidenciais (login, pagamentos), limite de taxa por usuário quando autenticado e sempre retorne claroRateLimit-* eRetry-After cabeçalhos para que os clientes recuem normalmente. Combine-o com um CDN/WAF para uma defesa profunda.
🔗 Share this article
✍️ Leave a Comment