Rate limiting no Nginx para APIs

Rate limiting limita quantas requisições um IP pode fazer em determinado período, proteção essencial contra brute force em endpoints de login, DDoS de baixo volume e scraping abusivo. No Nginx, configure em menos de 10 linhas com limit_req_zone e tenha proteção imediata para todas as rotas.

Configuração básica de rate limiting

limit_req_zone define a zona de rate limiting; limit_req aplica em rotas específicas:

nginx
# /etc/nginx/nginx.conf — bloco http (antes dos includes)
http {
    # Zona global: 10 req/s por IP, memória de 10MB (suporta ~160k IPs)
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

    # Zona específica para login: 5 req/min por IP
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;

    # Zona para endpoints públicos: 30 req/s por IP
    limit_req_zone $binary_remote_addr zone=public_limit:10m rate=30r/s;

    # Retornar 429 (Too Many Requests) em vez de 503
    limit_req_status 429;
}

Aplicar rate limiting por rota

Diferentes endpoints têm diferentes tolerâncias. Configure por rota:

nginx
# /etc/nginx/sites-available/minha-api

server {
    listen 443 ssl;
    server_name api.seudominio.com.br;

    # Endpoint de login: muito restritivo
    location /auth/login {
        limit_req zone=login_limit burst=3 nodelay;
        proxy_pass http://app:3000;
    }

    # API geral: moderado com burst
    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        proxy_pass http://app:3000;
    }

    # Webhook: sem rate limit (origem conhecida)
    location /webhooks/ {
        allow 192.0.2.0/24;   # IPs do serviço de webhook
        deny all;
        proxy_pass http://app:3000;
    }

    # Assets estáticos: sem rate limit
    location /static/ {
        root /var/www;
        expires 1y;
    }
}

Whitelist de IPs para rate limiting

Exclua do rate limiting IPs internos e serviços conhecidos:

nginx
# /etc/nginx/nginx.conf
http {
    # Mapa para whitelist de IPs
    geo $limit {
        default         1;         # aplicar rate limit
        127.0.0.1       0;         # localhost — sem limite
        10.0.0.0/8      0;         # rede interna — sem limite
        203.0.113.10    0;         # IP fixo do escritório — sem limite
    }

    # Usar mapa na zona: IPs na whitelist têm chave vazia (sem limite)
    map $limit $limit_key {
        0 "";
        1 $binary_remote_addr;
    }

    limit_req_zone $limit_key zone=api_limit:10m rate=10r/s;
}

# No server block, funciona normalmente:
# limit_req zone=api_limit burst=20;
# IPs da whitelist (limit_key="") não são contados

Respostas customizadas para rate limit excedido

Retorne JSON para APIs em vez da página de erro padrão do Nginx:

nginx
# /etc/nginx/sites-available/minha-api
server {
    # Resposta JSON para 429
    error_page 429 @rate_limit_exceeded;

    location @rate_limit_exceeded {
        add_header Content-Type application/json always;
        add_header Retry-After 60 always;
        return 429 '{"error":"rate_limit_exceeded","message":"Muitas requisições. Tente novamente em 60 segundos.","retry_after":60}';
    }

    # Logar requisições bloqueadas por rate limit
    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        limit_req_log_level warn;   # warn (padrão) ou error
        proxy_pass http://app:3000;
    }
}

Monitorar requisições bloqueadas

Verifique quantas requisições estão sendo bloqueadas:

bash
# Ver logs de rate limit em tempo real
tail -f /var/log/nginx/error.log | grep "limiting requests"

# Contar bloqueios por período
grep "limiting requests" /var/log/nginx/error.log | \
  grep "$(date +%Y/%m/%d)" | wc -l

# IPs mais bloqueados
grep "limiting requests" /var/log/nginx/error.log | \
  grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | \
  sort | uniq -c | sort -rn | head -10

# Verificar configuração atual
nginx -T | grep limit_req

Não quer configurar manualmente?

Implante o VPS em menos de 5 minutos com a Runstack. Infraestrutura da OPEN Datacenter, com servidores em São Paulo.

Perguntas frequentes

Sem nodelay: requisições do burst são enfileiradas e processadas na velocidade do rate (adiciona latência). Com nodelay: requisições do burst são processadas imediatamente, mas o contador é debitado. Exemplo com rate=10r/s e burst=20 nodelay: pode receber 30 req instantâneos (20 burst + 10 da taxa), depois limita a 10/s.

Para DDoS distribuído real (milhares de IPs), o rate limiting por IP no Nginx é insuficiente. Soluções: Cloudflare (plano gratuito já tem proteção DDoS básica), AWS Shield, ou Hetzner/OVH que absorvem tráfego antes de chegar ao servidor. Para VPS pequena: Cloudflare na frente é o padrão.

Meça o tráfego legítimo antes de aplicar limite. Verifique no access log: quantas requisições por segundo no pico do usuário mais ativo. Defina o limite 2-3x acima disso para usuários normais, mas restrito o suficiente para bloquear automação agressiva. Comece conservador e afrouxe conforme necessário.

Sim, se 100 usuários de uma empresa compartilham 1 IP público, o rate limit é aplicado ao IP, não ao usuário. Mitigações: rate limite por usuário autenticado (no backend, não no Nginx), limites mais altos para endpoints que sabidamente têm tráfego legítimo de alto volume.

Combine: rate limit por IP no Nginx (proteção sem autenticação) + rate limit por usuário autenticado no backend (mais preciso). Para Nginx autenticar JWT: módulo nginx-auth-jwt (pago/OpenResty) ou use a API do seu backend como auth_request. Para a maioria dos casos: rate limit por IP no Nginx + limite por usuário na aplicação.