A limitação de taxa é uma das primeiras linhas de defesa mais eficazes contra ataques DDoS, tentativas de brute force em login e abuso de APIs. O Nginx inclui dois módulos integrados poderosos — limit_req e limit_conn — que permitem controlar o tráfego de entrada sem instalar software de terceiros. Neste guia, você vai configurar ambos os módulos com exemplos prontos para produção, adicionar IPs confiáveis à lista de permissões e testar tudo antes de publicar.

Pré-requisitos

  • Nginx 1.18+ instalado em um servidor Linux (Ubuntu/Debian ou RHEL/CentOS)
  • Acesso root ou sudo
  • Familiaridade básica com blocos server do Nginx e sintaxe de configuração
  • Uma ferramenta para teste de carga: ab (Apache Bench), wrk ou curl

Entendendo a Limitação de Taxa no Nginx

O módulo limit_req do Nginx implementa o algoritmo de balde furado (leaky bucket). Imagine um balde com um pequeno furo no fundo: a água (requisições) entra em taxas variadas, mas escoa a uma taxa fixa. Se o balde transbordar, o excesso de água (requisições) é rejeitado.

Dois conceitos-chave orientam a configuração:

  • Zona: Uma área de memória compartilhada que rastreia contagens de requisições por chave (geralmente o IP do cliente). Definida uma vez no bloco http e referenciada nos blocos server ou location.
  • Taxa: A taxa de transferência permitida, expressa em requisições por segundo (r/s) ou por minuto (r/m). O Nginx internamente converte tudo para granularidade por segundo.

O módulo limit_conn funciona de forma diferente — ele limita o número de conexões simultâneas abertas a partir de uma única chave, em vez da taxa de requisições.

Configurando a Limitação de Taxa de Requisições

Comece definindo zonas no bloco http do /etc/nginx/nginx.conf:

http {
    # General zone: 10 requests/second per IP, 10 MB shared memory
    limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;

    # Strict zone for login endpoints: 1 request/second per IP
    limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

    # Return 429 instead of the default 503
    limit_req_status 429;
}

A variável $binary_remote_addr armazena IPs dos clientes em um formato binário compacto — 10 MB comportam aproximadamente 160.000 endereços.

Agora aplique essas zonas no seu bloco server:

server {
    listen 80;
    server_name example.com;

    # General rate limit for all requests
    location / {
        limit_req zone=general burst=20 nodelay;
        proxy_pass http://backend;
    }

    # Strict limit on login page
    location /wp-login.php {
        limit_req zone=login burst=3 nodelay;
        proxy_pass http://backend;
    }

    # Custom 429 error page
    error_page 429 /rate_limit.html;
    location = /rate_limit.html {
        internal;
        return 429 '{"error": "Too many requests. Please retry after a few seconds."}';
        default_type application/json;
    }
}

O parâmetro burst define quantas requisições excedentes podem entrar na fila antes que o Nginx comece a rejeitá-las. Com nodelay, as requisições em burst são processadas imediatamente em vez de serem espaçadas — isso proporciona uma melhor experiência para picos de tráfego legítimo.

Configurando Limites de Conexão

A limitação de conexão impede que um único IP mantenha muitos sockets abertos simultaneamente, que é o mecanismo central por trás de ataques no estilo slowloris:

http {
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    limit_conn_status 429;
}

server {
    # Max 10 simultaneous connections per IP
    limit_conn addr 10;

    # Limit bandwidth per connection (optional)
    limit_rate 100k;

    location /downloads/ {
        limit_conn addr 2;
        limit_rate 50k;
    }
}

Comparação de Limitação de Taxa

Recursolimit_reqlimit_connWAF Externo (Cloudflare/ModSecurity)
ControlaTaxa de requisições (req/s)Conexões simultâneasAmbos + padrões de Camada 7
AlgoritmoBalde furado (leaky bucket)Contador de conexõesVaria (ML, assinaturas)
GranularidadePor chave (IP, header, etc.)Por chavePor regra, geo, ASN
Impacto no desempenhoMínimoMínimoLeve latência adicionada
Eficácia contra DDoSBom para floods L7Bom para slowlorisMelhor para volumétrico
ConfiguraçãoApenas config do NginxApenas config do NginxServiço/agente separado
CustoGratuitoGratuitoPlano gratuito ou pago

Para a maioria dos servidores, combinar limit_req + limit_conn localmente com um WAF externo proporciona defesa em profundidade.

Cenário do Mundo Real

Você tem um servidor em produção executando WordPress atrás do Nginx. Seus logs de acesso mostram milhares de requisições POST atingindo /wp-login.php e /xmlrpc.php de IPs rotativos — um ataque de brute force distribuído. O uso de CPU está disparando e os usuários legítimos estão vendo timeouts.

Aqui está uma configuração de produção que resolve esse problema:

http {
    limit_req_zone $binary_remote_addr zone=wp_login:10m rate=1r/s;
    limit_req_zone $binary_remote_addr zone=xmlrpc:10m rate=1r/m;
    limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
    limit_req_status 429;
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
}

server {
    listen 443 ssl;
    server_name example.com;

    limit_conn conn_limit 15;

    location / {
        limit_req zone=general burst=50 nodelay;
        proxy_pass http://127.0.0.1:8080;
    }

    location = /wp-login.php {
        limit_req zone=wp_login burst=3 nodelay;
        proxy_pass http://127.0.0.1:8080;
    }

    # Block xmlrpc.php almost entirely — 1 request per minute
    location = /xmlrpc.php {
        limit_req zone=xmlrpc burst=1 nodelay;
        proxy_pass http://127.0.0.1:8080;
    }
}

Após aplicar essa configuração e executar nginx -t && systemctl reload nginx, o tráfego de brute force recebe respostas 429 enquanto os usuários legítimos continuam se autenticando normalmente.

Lista de Permissões de IPs Confiáveis

Sistemas de monitoramento, health checks e redes internas devem ignorar os limites de taxa. Use o módulo geo para criar uma lista de permissões:

http {
    geo $rate_limit_key {
        default         $binary_remote_addr;
        10.0.0.0/8      "";    # Internal network
        192.168.1.0/24   "";   # Office network
        203.0.113.50     "";   # Monitoring server
    }

    # Empty key = no tracking = no rate limit
    limit_req_zone $rate_limit_key zone=general:10m rate=10r/s;
}

Quando $rate_limit_key resolve para uma string vazia, o Nginx pula completamente o rastreamento de limitação de taxa para aquela requisição.

Testando Limites de Taxa

Sempre teste em staging antes de publicar em produção. Use limit_req_dry_run on para registrar violações sem bloquear:

location / {
    limit_req zone=general burst=20 nodelay;
    limit_req_dry_run on;  # Log only, don't block
}

Em seguida, gere tráfego com o Apache Bench:

# Send 100 requests with 10 concurrent connections
ab -n 100 -c 10 https://staging.example.com/

# Check how many were rate limited
grep "limiting requests" /var/log/nginx/error.log | wc -l

# Check 429 responses in access log
awk '$9 == 429' /var/log/nginx/access.log | wc -l

Ou use o wrk para testes de carga sustentados:

wrk -t4 -c50 -d30s https://staging.example.com/

Quando estiver satisfeito com os limites, remova a diretiva limit_req_dry_run e recarregue o Nginx.

Armadilhas e Casos Especiais

  • Clientes atrás de CDN/proxy: Se o Nginx está atrás do Cloudflare, de um load balancer ou de um proxy reverso, $binary_remote_addr será o IP do proxy — não do cliente. Use set_real_ip_from e real_ip_header para extrair o IP real do cliente:
set_real_ip_from 173.245.48.0/20;  # Cloudflare IP range
set_real_ip_from 103.21.244.0/22;
real_ip_header CF-Connecting-IP;
  • IPs compartilhados (NAT/corporativo): Limites de taxa agressivos podem bloquear escritórios inteiros atrás de um único IP NAT. Defina valores de burst razoáveis e adicione faixas de IP corporativas conhecidas à lista de permissões.
  • Esgotamento de memória da zona: Se a zona ficar sem memória, o Nginx retorna 503 para todas as novas chaves. Monitore o uso da zona e dimensione adequadamente — 1 MB comporta aproximadamente 16.000 endereços IP.
  • Mal-entendido sobre taxa vs burst: Uma taxa de 10r/s com burst=20 não permite 30 requisições por segundo. Ela permite uma fila de burst de 20 requisições que drenam a 10/s. Sem nodelay, as requisições na fila experimentam atraso artificial.
  • Ruído nos logs: Limitação de taxa em alto tráfego enche os logs de erro rapidamente. Use um error_log separado para locations com limitação de taxa ou ajuste os níveis de log.

Resumo

  • Use limit_req para controlar taxas de requisição com o algoritmo de balde furado — ideal para páginas de login, APIs e proteção geral contra floods.
  • Use limit_conn para limitar conexões simultâneas por IP — eficaz contra slowloris e ataques de esgotamento de recursos.
  • Sempre altere o código de status padrão para 429 com limit_req_status 429.
  • Adicione IPs internos à lista de permissões usando o módulo geo para que monitoramento e health checks nunca sejam bloqueados.
  • Teste com limit_req_dry_run on e ferramentas como ab ou wrk antes de aplicar em produção.
  • Considere proxies e CDNs configurando real_ip_header para identificar o IP real do cliente.
  • Combine a limitação de taxa do Nginx com Fail2ban e um WAF externo para defesa em profundidade.

Artigos Relacionados