Em 2004, os servidores web líderes de mercado — Apache em especial — tinham um problema estrutural que ficou famoso como o “C10K problem”: lidar com dez mil conexões simultâneas de forma eficiente. A maioria dos servidores criava um processo ou thread separado para cada conexão, o que funcionava razoavelmente bem com centenas de usuários simultâneos, mas começava a colapsar quando a escala chegava a milhares.
Igor Sysoev, engenheiro de software russo que trabalhava como administrador de sistemas, enfrentava esse problema diariamente. Em 2002, começou a desenvolver em paralelo um servidor web com uma premissa arquitetural radicalmente diferente: em vez de um processo por conexão, um modelo baseado em eventos e operações não-bloqueantes que permitia um único processo gerenciar milhares de conexões simultâneas com overhead mínimo.
Dois anos depois, em outubro de 2004, lançou o NGINX. Hoje o NGINX roda em mais de 30% de todos os sites ativos na internet — do pequeno blog hospedado em VPS até a infraestrutura da Netflix, Airbnb e Dropbox. Neste guia você vai entender como esse servidor funciona por dentro, como instalá-lo e configurá-lo em Ubuntu, CentOS e Windows, como configurar proxy reverso com balanceamento de carga, SSL/TLS, cache, compressão e proteção contra ataques DDoS. Cada seção traz configurações reais, não apenas teoria.
A arquitetura que torna o NGINX diferente
Compreender por que o NGINX resolve o problema de alta concorrência melhor que o Apache tradicional exige entender os dois modelos arquiteturais em jogo.
O Apache clássico usa o modelo prefork ou worker: para cada requisição, ele aloca um processo ou thread dedicado. Com 1.000 usuários simultâneos, o servidor mantém 1.000 processos ativos. Com 10.000, 10.000 processos — consumindo memória proporcional e gerando overhead massivo de troca de contexto pelo sistema operacional.
O NGINX usa um modelo de evento não-bloqueante: um número pequeno de worker processes (tipicamente igual ao número de núcleos de CPU do servidor) gerencia todas as conexões usando multiplexação de I/O. Quando uma conexão aguarda dados da rede ou do disco, o worker process não fica bloqueado esperando — ele passa para atender outra conexão e retorna quando os dados estiverem disponíveis.
💡 Dica: A consequência prática desse modelo é que o NGINX consome uma fração do consumo de memória do Apache para o mesmo volume de conexões simultâneas. Em benchmarks de alta concorrência, NGINX mantém throughput estável onde o Apache começa a degradar por esgotamento de recursos.
Além de servidor web, o NGINX funciona simultaneamente como:
- Proxy reverso: intermediário entre clientes e servidores backend, ocultando a infraestrutura interna
- Balanceador de carga: distribui requisições entre múltiplos servidores backend com diferentes algoritmos
- Cache HTTP: armazena respostas de servidores backend para servir requisições subsequentes sem tocar o backend
- Proxy de email: suporta protocolos IMAP, POP3 e SMTP para infraestruturas de email
Instalação passo a passo
Ubuntu e Debian
# Atualize os índices de pacotes
sudo apt update
# Instale o NGINX
sudo apt install nginx
# Verifique o status do serviço
sudo systemctl status nginx
# Inicie o serviço (se não iniciou automaticamente)
sudo systemctl start nginx
# Habilite início automático com o sistema
sudo systemctl enable nginx
# Teste abrindo http://localhost — você verá a página de boas-vindas do NGINXCentOS e RHEL
# Adicione o repositório EPEL
sudo yum install epel-release
# Instale o NGINX
sudo yum install nginx
# Verifique o status
sudo systemctl status nginx
# Inicie e habilite o serviço
sudo systemctl start nginx
sudo systemctl enable nginx
# Configure o firewall para permitir tráfego HTTP e HTTPS
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reloadWindows
No Windows, o NGINX opera como processo de linha de comando sem integração com o gerenciador de serviços do sistema:
# Baixe o pacote ZIP do site oficial (nginx.org/en/download.html)
# Extraia para C:\nginx
# Abra o Prompt de Comando como Administrador e navegue até o diretório
cd C:\nginx
# Inicie o NGINX
nginx.exe
# Recarregue a configuração sem parar o servidor
nginx.exe -s reload
# Pare o NGINX graciosamente (aguarda conexões ativas terminarem)
nginx.exe -s quit
# Pare o NGINX imediatamente
nginx.exe -s stop⚠️ Atenção: O suporte ao Windows é funcional mas limitado — o modelo de eventos do NGINX no Windows usa select() em vez dos mecanismos mais eficientes disponíveis no Linux (epoll) e macOS (kqueue). Para produção, hosts Linux oferecem performance significativamente superior.
Configurações básicas pós-instalação
Após a instalação, verifique e ajuste a estrutura de diretórios principal:
/etc/nginx/ → diretório raiz de configurações
/etc/nginx/nginx.conf → arquivo de configuração principal
/etc/nginx/conf.d/ → configurações adicionais incluídas pelo nginx.conf
/var/www/html/ → diretório padrão para conteúdo web
/var/log/nginx/access.log → log de todas as requisições HTTP
/var/log/nginx/error.log → log de erros e advertências
Verifique os logs imediatamente após a instalação para confirmar que o serviço iniciou sem erros:
sudo tail -f /var/log/nginx/error.log
O arquivo nginx.conf: entendendo cada bloco
O arquivo de configuração principal do NGINX usa uma estrutura hierárquica de blocos que determina o escopo de cada diretiva:
# Contexto global — se aplica ao processo NGINX inteiro
user www-data;
worker_processes auto; # 'auto' usa um worker por núcleo de CPU
pid /run/nginx.pid;
events {
worker_connections 1024; # Conexões máximas por worker process
multi_accept on; # Cada worker aceita múltiplas conexões por vez
}
http {
# Configurações que se aplicam a todo tráfego HTTP
sendfile on; # Envio eficiente de arquivos via syscall do kernel
tcp_nopush on; # Otimiza envio de headers junto com início dos dados
tcp_nodelay on; # Reduz latência em conexões keep-alive
keepalive_timeout 65; # Mantém conexões abertas por 65 segundos
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on; # Compressão de respostas — detalhado mais adiante
include /etc/nginx/conf.d/*.conf; # Inclui configurações de sites individuais
}Crie um arquivo separado em /etc/nginx/conf.d/meusite.conf para cada site ou aplicação que o NGINX serve — isso mantém a configuração organizada e facilita habilitar ou desabilitar sites individualmente:
server {
listen 80;
server_name meusite.com www.meusite.com;
root /var/www/meusite;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}Após qualquer modificação de configuração, verifique a sintaxe antes de reiniciar:
sudo nginx -t # Testa a configuração
sudo systemctl reload nginx # Recarrega sem interromper conexões ativasProxy reverso: redirecionando tráfego para servidores backend
O proxy reverso é provavelmente o uso mais comum do NGINX em produção. Em vez de expor diretamente o servidor de aplicação (Node.js, Django, Rails, Spring Boot) à internet, o NGINX recebe todas as requisições externas e as repassa para o backend internamente.
Configuração básica de proxy reverso
server {
listen 80;
server_name meusite.com;
location / {
proxy_pass http://localhost:3000; # Backend Node.js rodando localmente
# Headers essenciais para o backend receber informações corretas do cliente
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Suporte a WebSockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
}
}O header X-Real-IP passa o endereço IP real do cliente para o backend — sem ele, o backend veria o IP do NGINX como origem de todas as requisições, o que quebra qualquer lógica de rate limiting ou geolocalização no nível da aplicação.
NGINX como proxy para frameworks específicos
Django com Gunicorn:
server {
listen 80;
server_name meusite.com;
location / {
proxy_pass http://unix:/var/run/gunicorn.sock;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Arquivos estáticos servidos diretamente pelo NGINX (muito mais eficiente)
location /static/ {
alias /var/www/meusite/staticfiles/;
}
}PHP com PHP-FPM:
server {
listen 80;
server_name meusite.com;
root /var/www/meusite;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
# Processa arquivos PHP via FastCGI
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
}
# Bloqueia acesso a arquivos .htaccess (específicos do Apache)
location ~ /\.ht {
deny all;
}
}Balanceamento de carga entre múltiplos servidores
O NGINX distribui tráfego entre múltiplos servidores backend usando o bloco upstream. Três algoritmos principais cobrem a maioria dos casos de uso:
Round-robin (padrão): distribuição equitativa
upstream backend {
server backend1.exemplo.com;
server backend2.exemplo.com;
server backend3.exemplo.com;
}
server {
listen 80;
server_name meusite.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}Least connections: direciona para o servidor menos sobrecarregado
upstream backend {
least_conn;
server backend1.exemplo.com;
server backend2.exemplo.com;
server backend3.exemplo.com;
}Mais eficiente que round-robin quando requisições têm tempo de processamento variável — servidores rápidos recebem mais tráfego automaticamente.
IP hash: garante que o mesmo cliente sempre vai para o mesmo servidor
upstream backend {
ip_hash;
server backend1.exemplo.com;
server backend2.exemplo.com;
}Útil para aplicações que mantêm sessão de usuário no servidor — sem IP hash, o mesmo usuário pode cair em servidores diferentes a cada requisição, perdendo o estado de sessão.
💡 Dica: Adicione pesos quando os servidores têm capacidades diferentes (server backend1.exemplo.com weight=3;), e use a diretiva backup para servidores de failover que só recebem tráfego quando todos os primários estão fora: server backup.exemplo.com backup;.
SSL/TLS: configurando HTTPS corretamente
HTTPS não é opcional em 2025 — navegadores marcam sites HTTP como inseguros, e o Google usa HTTPS como sinal de ranqueamento. Esta configuração cobre todos os requisitos de um servidor HTTPS moderno:
# Redireciona todo tráfego HTTP para HTTPS
server {
listen 80;
server_name meusite.com www.meusite.com;
return 301 https://$host$request_uri;
}
# Bloco HTTPS principal
server {
listen 443 ssl http2;
server_name meusite.com www.meusite.com;
# Caminhos para o certificado e chave privada
ssl_certificate /etc/nginx/ssl/meusite.crt;
ssl_certificate_key /etc/nginx/ssl/meusite.key;
# Apenas versões seguras do TLS
ssl_protocols TLSv1.2 TLSv1.3;
# Ciphers modernos e seguros
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# Cache de sessão SSL — melhora performance em conexões repetidas
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# HSTS — instrui navegadores a só usarem HTTPS por 1 ano
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Cabeçalhos de segurança adicionais
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
root /var/www/meusite;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}⚠️ Atenção: Nunca use SSL v3, TLS 1.0 ou TLS 1.1 — esses protocolos têm vulnerabilidades conhecidas e exploráveis (POODLE, BEAST). TLS 1.2 com ciphers modernos e TLS 1.3 são os únicos protocolos aceitáveis para produção. Ferramentas como SSL Labs (ssllabs.com/ssltest/) verificam a qualidade da sua configuração SSL gratuitamente.
Para usar certificados gratuitos do Let’s Encrypt, instale o Certbot que configura e renova automaticamente:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d meusite.com -d www.meusite.com
# O Certbot modifica automaticamente o nginx.conf e configura renovação automáticaCache: reduzindo carga no backend e latência para o usuário
Configuração de cache de proxy
http {
# Define onde o cache fica e seu tamanho máximo
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=meu_cache:10m
max_size=10g
inactive=60m
use_temp_path=off;
server {
listen 80;
server_name meusite.com;
location / {
proxy_cache meu_cache;
proxy_pass http://backend_server;
# Tempo de validade do cache por código de resposta
proxy_cache_valid 200 1h; # Cache respostas 200 por 1 hora
proxy_cache_valid 301 24h; # Cache redirecionamentos por 24 horas
proxy_cache_valid any 1m; # Qualquer outro código por 1 minuto
# Adiciona header mostrando se a resposta veio do cache
add_header X-Cache-Status $upstream_cache_status;
# Não faz cache quando o cliente envia esses headers
proxy_cache_bypass $http_pragma $http_authorization;
}
}
}O header X-Cache-Status que você adiciona aceita os valores HIT (respondido do cache), MISS (foi ao backend), BYPASS (ignorou o cache) e EXPIRED (cache expirado, foi ao backend). Use esse header para verificar se o cache está funcionando corretamente.
Cache de conteúdo estático com Expires
Para arquivos estáticos que raramente mudam, configure um tempo de cache longo diretamente nos headers da resposta:
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
expires 30d; # Navegador caches por 30 dias
add_header Cache-Control "public, no-transform";
}Compressão gzip: reduzindo o tamanho das respostas
Compressão gzip reduz o tamanho de transferência de arquivos de texto (HTML, CSS, JavaScript, JSON) em 60-80%, acelerando carregamento de páginas e reduzindo consumo de banda:
http {
gzip on;
gzip_comp_level 5; # Nível 1-9; 5 equilibra compressão e CPU
gzip_min_length 256; # Não comprime respostas menores que 256 bytes
gzip_vary on; # Adiciona header Vary para proxies intermediários
gzip_proxied any; # Comprime também respostas de proxies upstream
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
}Não ative gzip para imagens JPEG, PNG ou outros formatos binários — esses formatos já são comprimidos e tentar comprimir novamente desperdiça CPU sem reduzir o tamanho.
Segurança: proteção contra DDoS e ataques comuns
Rate limiting: limitando requisições por IP
http {
# Define zona de limite: 10MB para armazenar estado por IP
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;
server {
# Limite geral: 10 requisições/segundo com burst de 20
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://backend;
}
# Limite mais restrito para endpoint de login
location /login {
limit_req zone=login_limit burst=5;
proxy_pass http://backend
}
}
}Limitando conexões simultâneas por IP
http {
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
location / {
limit_conn conn_limit 10; # Máximo 10 conexões simultâneas por IP
proxy_pass http://backend;
}
}
}Bloqueando métodos HTTP não utilizados
server {
# Aceita apenas GET, POST e HEAD; retorna 405 para qualquer outro
if ($request_method !~ ^(GET|POST|HEAD)$) {
return 405;
}
}Protegendo arquivos e diretórios sensíveis
# Bloqueia acesso a arquivos de configuração e ocultos
location ~ /\.(ht|git|env) {
deny all;
}
# Bloqueia acesso a arquivos de backup
location ~ \.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)$ {
deny all;
}Otimização de performance: configurações que fazem diferença
Keep-alive e configurações de conexão
http {
keepalive_timeout 65; # Mantém conexão aberta por 65 segundos
keepalive_requests 100; # Máximo de requisições por conexão keep-alive
sendfile on; # Usa syscall sendfile() do kernel — mais eficiente
tcp_nopush on; # Agrupa headers de resposta em um único pacote
tcp_nodelay on; # Desativa algoritmo Nagle para conexões keep-alive
# Buffers de requisição e resposta
client_body_buffer_size 16k;
client_max_body_size 8m; # Tamanho máximo de upload
client_header_buffer_size 1k;
large_client_header_buffers 4 16k;
# Timeouts
client_body_timeout 12;
client_header_timeout 12;
send_timeout 10;
}Ajuste de worker processes e conexões
# Worker processes: 'auto' usa um por núcleo de CPU — ideal para a maioria dos casos
worker_processes auto;
# Para servidores com muitos arquivos estáticos, ajuste o limite de arquivos abertos
worker_rlimit_nofile 65535;
events {
worker_connections 4096; # Conexões simultâneas por worker process
multi_accept on; # Cada worker aceita múltiplas conexões por iteração
use epoll; # Mecanismo de I/O mais eficiente no Linux
}Monitoramento e diagnóstico
Habilitando o módulo de status
server {
listen 80;
server_name localhost;
location /nginx_status {
stub_status on;
allow 127.0.0.1; # Acesso apenas de localhost
deny all;
}
}Acesse http://localhost/nginx_status para ver conexões ativas, requisições por segundo e outros indicadores básicos de saúde do servidor.
Ferramentas de monitoramento externas
NGINX Amplify (saas.amplify.nginx.com) oferece monitoramento detalhado específico para NGINX com dashboards, alertas e análise de configuração. A versão básica é gratuita para um servidor.
Prometheus + Grafana com o exporter nginx-prometheus-exporter expõe métricas do NGINX no formato Prometheus, permitindo criar dashboards personalizados e alertas baseados em thresholds específicos — adequado para times que já usam esse stack de observabilidade.
Perguntas frequentes sobre NGINX
O Apache usa modelo de processo/thread por conexão, o que o torna mais flexível para módulos e configuração por diretório via .htaccess, mas menos eficiente em alta concorrência. O NGINX usa modelo de eventos não-bloqueante que escala muito melhor para muitas conexões simultâneas com uso menor de memória. Na prática: prefira NGINX para servir conteúdo estático em alta escala, proxy reverso e balanceamento de carga. Prefira Apache quando você precisa de módulos específicos do Apache (como mod_rewrite em configurações complexas de .htaccess) ou para aplicações PHP legadas que assumem Apache. Muitas arquiteturas usam os dois juntos: NGINX na frente como proxy reverso e Apache atrás para executar PHP.
Use sudo nginx -t para verificar a sintaxe da configuração nova. Se o teste passar, execute sudo systemctl reload nginx (ou sudo nginx -s reload). Esse comando instrui o NGINX a recarregar a configuração graciosamente — o master process cria novos worker processes com a configuração atualizada e aguarda os worker processes antigos terminarem as conexões em andamento antes de encerrá-los. O tráfego em curso nunca é interrompido.
Não — são ferramentas para propósitos distintos. Gunicorn, Unicorn, uWSGI e similares executam código de aplicação (Python, Ruby, etc.). O NGINX não executa código de aplicação; ele recebe conexões HTTP, as encaminha para o servidor de aplicação via proxy, e serve o resultado. A combinação padrão é NGINX na frente (recebendo conexões da internet, servindo arquivos estáticos, fazendo SSL termination) com o servidor de aplicação atrás (executando a lógica de negócio).
O caso mais comum é usar a imagem oficial nginx:alpine como container que serve conteúdo estático ou como proxy reverso para outros containers. Em um docker-compose.yml, o NGINX recebe conexões externas na porta 80/443 e encaminha para containers de aplicação na rede interna do Docker. Use volumes para montar o arquivo nginx.conf e os certificados SSL no container, e a diretiva proxy_pass http://nome-do-servico:porta para encaminhar tráfego para outros containers usando os nomes de serviço como hostname na rede Docker.
Adicione o header add_header X-Cache-Status $upstream_cache_status; na configuração. Faça uma requisição e inspecione os headers da resposta — X-Cache-Status: MISS indica que o NGINX foi ao backend; X-Cache-Status: HIT indica que serviu do cache. A primeira requisição para uma URL sempre gera MISS; a segunda deve gerar HIT se o cache estiver configurado corretamente. Use curl -I https://meusite.com/recurso para inspecionar headers de resposta rapidamente sem abrir o navegador.
Conclusão
O NGINX resolveu um problema técnico real — como um servidor web com um único processo gerencia dez mil conexões simultâneas — e essa solução elegante se tornou a infraestrutura silenciosa de uma fração enorme da internet moderna. Entender a arquitetura orientada a eventos que o torna eficiente em alta concorrência explica tanto por que ele existe quanto onde ele se encaixa melhor.
Três pontos resumem o essencial deste guia. Primeiro, o NGINX raramente opera apenas como servidor web — sua força real emerge quando você o configura como proxy reverso com SSL termination na frente de servidores de aplicação, separando a responsabilidade de gerenciar conexões HTTP (onde NGINX é excelente) da responsabilidade de executar lógica de aplicação (onde Gunicorn, Node.js ou Spring Boot são mais adequados). Segundo, configurações de segurança como rate limiting, limitação de conexões por IP e bloqueio de métodos HTTP não utilizados devem ser implementadas desde o início, não depois de um incidente. Terceiro, verificar sempre a configuração com nginx -t antes de aplicar mudanças e usar reload em vez de restart são hábitos que evitam interrupções desnecessárias em produção.
O arquivo de configuração do NGINX parece intimidador até você entender que ele é composto por blocos hierárquicos com semântica clara. Depois disso, configurar proxy reverso, balanceamento de carga e HTTPS se torna um processo direto — e você passa a entender por que tantas equipes confiam nessa ferramenta criada por um administrador de sistemas frustrado com servidores lentos em 2002.
Se este guia ajudou você a configurar o NGINX com mais confiança, compartilhe com o time de infraestrutura. Configuração correta desde o início economiza muito mais tempo do que debug de problemas de performance em produção.
Uma resposta