Usar o Nginx como proxy para Node.js permite colocar uma camada especializada em HTTP, TLS, arquivos estáticos, compressão, cache e balanceamento diante da aplicação. O Node.js continua responsável pela lógica de negócio, enquanto o Nginx aceita conexões públicas, encaminha requisições e aplica políticas operacionais.
Essa arquitetura facilita certificados, múltiplos processos, limites de upload, timeouts e deploys sem expor diretamente a porta da aplicação. Porém, configurações incorretas podem remover o IP real, quebrar WebSocket, duplicar compressão ou permitir spoofing de headers encaminhados.
Neste guia, você aprenderá a configurar proxy_pass, headers, TLS, WebSocket, SSE, balanceamento, health checks, timeouts, cache, segurança, logs e graceful shutdown.
O que é um proxy reverso?
Um proxy reverso recebe a requisição do cliente e a encaminha para um servidor interno. A documentação oficial do Nginx sobre reverse proxy apresenta diretivas e exemplos. O RFC 7239 sobre Forwarded descreve informações de proxy em HTTP.
Para TLS na aplicação, consulte HTTPS e TLS no Node.js. Para gerenciamento de processos, veja PM2 em produção com Node.js.
Arquitetura básica
Internet
↓
Nginx :443
↓
Node.js :3000A porta 3000 pode ficar disponível apenas no loopback ou em rede privada.
Servidor Node.js
const http = require('node:http');
const server = http.createServer((req, res) => {
res.setHeader('content-type', 'application/json');
res.end(JSON.stringify({ status: 'ok' }));
});
server.listen(3000, '127.0.0.1');Vincular ao loopback impede acesso direto pela rede externa no mesmo host.
Configuração mínima do Nginx
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
}Valide antes de recarregar:
sudo nginx -t
sudo systemctl reload nginxHeaders encaminhados
location / {
proxy_pass http://127.0.0.1:3000;
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;
}Esses headers ajudam a aplicação a conhecer host, protocolo e IP original.
Não confie em headers externos
O Nginx deve substituir, não apenas preservar, os headers relevantes. Se a aplicação é acessível diretamente, um cliente pode forjar X-Forwarded-For.
Trust proxy no Express
app.set('trust proxy', 'loopback');Evite true sem entender quantos proxies existem. Uma configuração ampla pode fazer a aplicação confiar em IP e protocolo enviados pelo cliente.
Host original
O header Host pode influenciar geração de links, redirects e seleção de tenant. Valide nomes aceitos e não gere URLs absolutas com host arbitrário.
TLS no Nginx
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
}
}Proteja a chave privada e automatize renovação de certificados.
Redirecionando HTTP para HTTPS
server {
listen 80;
server_name api.example.com;
return 301 https://$host$request_uri;
}Use HSTS somente depois de confirmar que todos os subdomínios necessários suportam HTTPS.
Timeouts
location / {
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
send_timeout 30s;
proxy_pass http://127.0.0.1:3000;
}Alinhe timeouts entre cliente, Nginx e Node.js. Um proxy que espera 60 segundos diante de uma aplicação com prazo de 5 pode manter conexões inúteis.
Uploads
client_max_body_size 10m;Esse limite rejeita corpos grandes antes de chegarem ao Node.js. A aplicação ainda precisa validar tamanho real, tipo e conteúdo.
Buffering
Por padrão, o Nginx pode armazenar respostas do upstream em buffers. Isso protege o Node.js de clientes lentos, mas pode atrasar streaming.
proxy_buffering on;Server-Sent Events
Para SSE, desative buffering na rota:
location /events {
proxy_pass http://127.0.0.1:3000;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 1h;
}
Consulte Server-Sent Events com Node.js.
WebSocket
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
location /socket {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}Sem Upgrade e Connection, o handshake pode falhar.
Veja WebSocket com Node.js.
Keep-alive com upstream
upstream node_backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://node_backend;
}
}Conexões reutilizadas reduzem handshakes entre Nginx e aplicação.
Balanceamento entre processos
upstream node_backend {
least_conn;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
server 127.0.0.1:3003;
}least_conn envia novas requisições ao servidor com menos conexões ativas.
Round robin
Sem diretiva adicional, o Nginx usa round robin. Funciona bem quando instâncias possuem capacidade semelhante e requisições têm duração parecida.
Estado de sessão
Não mantenha sessão apenas na memória de uma instância. Use cookies assinados, Redis ou banco compartilhado. Assim, qualquer processo atende a próxima requisição.
Sticky sessions
Afinidade pode ser necessária em aplicações legadas, mas reduz flexibilidade e distribuição. Para WebSocket, a conexão permanece na mesma instância depois do upgrade, sem precisar reescolher durante a sessão.
Health endpoint
app.get('/health/live', (req, res) => {
res.status(200).json({ status: 'ok' });
});Uma rota de readiness pode verificar dependências críticas com timeout curto.
Consulte Health checks no Node.js.
Falha de upstream
O Nginx retorna 502 quando não consegue conectar ao Node.js. Logs devem diferenciar falha de conexão, timeout e resposta inválida.
proxy_next_upstream
proxy_next_upstream error timeout http_502 http_503;
proxy_next_upstream_tries 2;Retries automáticos são mais seguros em GET e outras operações idempotentes. Não repita POST de pagamento sem chave de idempotência.
Veja Idempotência em APIs Node.js.
Graceful shutdown
Durante deploy, a instância deve parar de receber trabalho, concluir conexões e encerrar. Consulte Graceful Shutdown no Node.js.
Removendo uma instância
Em uma implantação simples:
- remova ou marque o upstream;
- recarregue o Nginx;
- aguarde conexões terminarem;
- encerre o processo;
- suba a nova versão;
- recoloque no pool.
Reload do Nginx
nginx -s reload ou systemd recarregam configuração sem derrubar conexões existentes, desde que a configuração seja válida.
Arquivos estáticos
location /assets/ {
root /srv/app/public;
try_files $uri =404;
expires 1y;
add_header Cache-Control "public, immutable";
}Use nomes com hash para validade longa.
Compressão no Nginx
gzip on;
gzip_types application/json text/css application/javascript;
gzip_min_length 1024;Se o Nginx comprime, desative compressão duplicada no Node.js.
Consulte Compressão HTTP no Node.js.
Cache de proxy
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m;
location /public-api/ {
proxy_cache api_cache;
proxy_cache_valid 200 60s;
proxy_pass http://node_backend;
}Cache apenas respostas públicas. Respostas autenticadas exigem regras explícitas.
Cache-Control e ETag
O Node.js pode definir Cache-Control e ETag, e o Nginx respeitar ou ajustar. Consulte ETag e Cache HTTP no Node.js.
CORS
Centralizar CORS no Nginx pode funcionar, mas regras dinâmicas e autenticação costumam pertencer à aplicação. Evite configurar em ambos os lugares com valores divergentes.
Headers de segurança
Nginx pode adicionar:
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy no-referrer always;Content Security Policy costuma depender da aplicação e de nonces. Veja Helmet e CSP no Node.js.
Ocultando versão
server_tokens off;Isso reduz informação exposta, mas não substitui atualizações.
Limite de conexões
Use limites no Nginx e na aplicação para evitar que um cliente monopolize recursos. Rate limiting pode ser aplicado por IP ou chave, considerando proxies confiáveis.
Logs de acesso
log_format main '$remote_addr $request_id $request_method $uri '
'$status $request_time $upstream_response_time';Não registre query strings com tokens ou dados pessoais.
ID de requisição
O Nginx pode gerar ou encaminhar um ID:
proxy_set_header X-Request-ID $request_id;A aplicação inclui o mesmo valor em logs e resposta, após validação de formato.
Logs do Node.js
Use logs estruturados em stdout. O gerenciador de processos ou agente coleta. Não faça o Nginx e o Node.js registrarem corpos completos.
Endereço real em containers
Em Docker ou Kubernetes, o upstream pode ser um nome DNS interno. Considere resolução, mudança de IP e política de atualização do Nginx.
Docker Compose
upstream node_backend {
server app:3000;
}O serviço app é resolvido pela rede do Compose.
Kubernetes
Em Kubernetes, um Ingress Controller frequentemente substitui um Nginx instalado manualmente no pod. Mantenha a aplicação independente da implementação do proxy.
HTTP/2
O cliente pode usar HTTP/2 com Nginx, enquanto o upstream usa HTTP/1.1. Isso é comum e simplifica a aplicação.
Timeout do Node.js
Configure requestTimeout, headersTimeout e keep-alive de forma compatível com o proxy. Valores incoerentes causam resets.
Observabilidade
Monitore:
- status 499, 502, 503 e 504;
- tempo total;
- tempo de upstream;
- conexões ativas;
- fila;
- bytes enviados;
- cache hit;
- recarregamentos com erro.
Testes
Cubra:
- HTTP para HTTPS;
- IP real;
- Host permitido;
- upload acima do limite;
- timeout;
- WebSocket;
- SSE;
- instância indisponível;
- recarregamento;
- headers de cache.
Erros comuns
- Trust proxy amplo: cliente forja IP e HTTPS.
- Porta Node pública: regras do Nginx são contornadas.
- WebSocket sem Upgrade: conexão falha.
- Buffering em SSE: eventos atrasam.
- Timeouts desalinhados: conexões resetam.
- Compressão dupla: CPU e resposta são prejudicadas.
- Cache de conteúdo privado: dados vazam.
Boas práticas
- Mantenha Node.js em rede privada.
- Substitua headers encaminhados.
- Configure trust proxy restrito.
- Alinhe timeouts.
- Defina limites de corpo.
- Trate WebSocket e SSE separadamente.
- Use graceful shutdown.
- Centralize compressão.
- Valide configuração antes do reload.
- Monitore upstreams e status.
Conclusão
Usar o Nginx como proxy para Node.js separa responsabilidades: o proxy gerencia conexões públicas, TLS, arquivos, compressão e balanceamento, enquanto a aplicação executa regras de negócio.
O resultado é mais confiável quando headers, timeouts, buffering, cache e confiança em proxies são configurados explicitamente. Com a porta do Node.js protegida, logs correlacionados e deploy gradual, o Nginx oferece uma camada operacional robusta sem esconder falhas da aplicação.



