O problema
Se você tem site pro seu servidor de jogo (loja de VIP, painel de admin, fórum), vai receber:
- Scrapers puxando conteúdo pra revender
- Brute force tentando adivinhar login admin
- Bots de spam enchendo formulários
- DDoS de aplicação (Layer 7)
Rate limiting no Nginx = limitar quantas requisições cada IP pode fazer por segundo/minuto. Barato, eficaz, embutido.
Como funciona (conceito)
Nginx conta requests por IP numa "leaky bucket" (balde furado):
- Bucket enche a cada request
- Fura constantemente na taxa que você define
- Se transbordar: request rejeitada (HTTP 503)
Setup básico
Adicione em /etc/nginx/nginx.conf dentro do bloco http:
http {
# Zona compartilhada de rate limit (10 MB armazena ~160k IPs)
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
# Limita conexões simultâneas por IP
limit_conn_zone $binary_remote_addr zone=addr:10m;
# Custom response
limit_req_status 429;
limit_conn_status 429;
# ...resto do config...
}
Cada limit_req_zone cria uma "zona" nomeada. Explicação:
$binary_remote_addr— chave (IP do cliente em formato binário compacto)zone=general:10m— nome + 10 MB de RAMrate=10r/s— 10 requests por segundo (sustained)
Aplicar por rota
No seu server block:
server {
server_name loja.meuservidor.com.br;
# Regra geral (10 req/s)
location / {
limit_req zone=general burst=20 nodelay;
limit_conn addr 10;
try_files $uri $uri/ /index.php?$args;
}
# Login: 1 req/s (protege brute force)
location = /wp-login.php {
limit_req zone=login burst=3 nodelay;
include fastcgi_params;
# ...
}
# API: 30 req/s
location /api/ {
limit_req zone=api burst=50 nodelay;
proxy_pass http://backend;
}
# Assets estáticos: sem limit
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2)$ {
expires 30d;
}
}
Explicação dos parâmetros
burst=20— permite pico de 20 req antes de aplicar rate limitnodelay— processa o burst imediatamente (senão fica em fila)limit_conn addr 10— cada IP pode ter no máx 10 conexões abertas simultâneas
Casos de uso reais
Proteger /wp-login.php (WordPress)
Cada tentativa custa 1 req. Bot tentando 1000 senhas/min = bloqueado em segundos:
location = /wp-login.php {
limit_req zone=login burst=3 nodelay;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
}
Proteger /api/checkout
Bot testando roubo de cartão? Limita a 5/min:
limit_req_zone $binary_remote_addr zone=checkout:10m rate=5r/m;
# ...
location /api/checkout {
limit_req zone=checkout burst=2 nodelay;
proxy_pass http://backend;
}
Proteger formulário de contato
Uma pessoa não envia 20 msg em 1s. Bot sim:
limit_req_zone $binary_remote_addr zone=contact:5m rate=2r/m;
location /contato/enviar {
limit_req zone=contact burst=1;
# ...
}
Whitelist de IPs confiáveis
Não quer limitar seu próprio IP (admin) ou o Cloudflare?
geo $limit_ip {
default 1;
192.168.1.0/24 0; # rede local
173.245.48.0/20 0; # CF
103.21.244.0/22 0; # CF
}
map $limit_ip $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=general:10m rate=10r/s;
Quando $limit_key é vazio, o Nginx não conta aquele IP.
Combinar com Fail2Ban (banimento persistente)
Rate limit devolve 429 mas ainda usa CPU. Fail2Ban bane no firewall:
/etc/fail2ban/jail.d/nginx-req-limit.conf:
[nginx-req-limit]
enabled = true
filter = nginx-req-limit
action = iptables-multiport[name=ReqLimit, port="http,https", protocol=tcp]
logpath = /var/log/nginx/*error.log
findtime = 600
bantime = 3600
maxretry = 10
/etc/fail2ban/filter.d/nginx-req-limit.conf:
[Definition]
failregex = limiting requests, excess:.* by zone.*client: <HOST>
ignoreregex =
Reinicie: systemctl restart fail2ban.
Agora IPs que excederem 10x em 10min ficam banidos 1h no firewall (nem chegam ao Nginx).
Testes com ab (Apache Bench)
Simule ataque no seu próprio site:
apt install -y apache2-utils
ab -n 100 -c 20 https://meuservidor.com.br/
-n 100— total de requests-c 20— 20 concorrentes
Se rate limit está funcionando: você vai ver várias respostas 429.
Detectar problemas
Log de rate limit no Nginx:
tail -f /var/log/nginx/error.log | grep "limiting requests"
Ver contagem de 429 no access log:
grep " 429 " /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
Isso mostra top 20 IPs bloqueados.
Combos que fazem sentido
Loja de VIP simples
/ → general (10r/s)
/checkout → checkout (5r/m)
/api/* → api (30r/s)
Site com admin WordPress
/ → general
/wp-login → login (1r/s, burst 3)
/wp-json → api (30r/s)
/xmlrpc.php → hard block (bloquear 100%, deprecado)
Painel Pterodactyl
/ → general
/api/* → api (60r/s pra dashboard)
/login → login (2r/s)
Erros comuns
"Rate limit não funciona"
Confira ordem: limit_req_zone deve estar em http (nginx.conf), limit_req em location.
"Bloqueou meu próprio acesso"
Aumente rate. Ou adicione whitelist (geo).
"Nginx 502 aleatório"
Não relacionado a rate limit (503/429 são de RL). 502 é backend PHP/proxy travando.
"Cloudflare mascara IP real do cliente"
Sem real_ip_header, todos requests aparecem como o IP do Cloudflare. Configure:
real_ip_header CF-Connecting-IP;
set_real_ip_from 173.245.48.0/20;
# ... resto dos ranges de CF
Ganhos práticos
Com config sensata:
- -70% de tráfego lixo (bots vão bater 429 e desistir)
- CPU do PHP-FPM ~40% menor (menos requests processadas)
- Login brute force impossível na prática
- Você continua com mesma UX (usuários normais nunca batem no limite)
Conclusão
Rate limit no Nginx é must-have pra qualquer site público. Custa 15 minutos pra configurar, protege pra sempre.
Combinado com Fail2Ban vira defesa profissional. Combinado com Cloudflare (Layer 7 na frente) fica praticamente impenetrável.
Nossos VPS-BR rodam Nginx pré-instalado nas imagens Ubuntu.

