Configuração de Proxy Reverso Nginx: Direcionando Tráfego de Forma Eficiente

Configure um proxy reverso Nginx com roteamento claro, cabeçalhos corretos, suporte a WebSocket, timeouts, buffering e etapas de solução de problemas.

Configuração de Proxy Reverso Nginx: Direcionando Tráfego de Forma Eficiente

Uma configuração de proxy reverso Nginx permite que o Nginx receba tráfego web público e o encaminhe para uma ou mais aplicações backend. Isso é útil quando sua aplicação roda em Node.js, Python, Go, Java ou outro serviço que não deve ser exposto diretamente à internet.

Em vez de os usuários se conectarem à porta da sua aplicação, eles se conectam ao Nginx nas portas HTTP ou HTTPS padrão. O Nginx lida com a borda pública e então direciona o tráfego de forma eficiente para o serviço interno correto.

O que um Proxy Reverso Faz

Um proxy reverso fica na frente dos seus servidores de aplicação. O cliente fala com o Nginx, e o Nginx fala com o backend. Para o navegador, o Nginx é o site. Para a aplicação, o Nginx é o cliente upstream, a menos que você passe cabeçalhos que preservem os detalhes originais da requisição.

Esse padrão oferece vários benefícios:

  • Você pode executar aplicações em portas privadas como 3000, 5000 ou 8080.
  • Você pode terminar o TLS no Nginx.
  • Você pode rotear diferentes nomes de host ou caminhos para diferentes serviços.
  • Você pode adicionar buffering, timeouts, compressão e cache.
  • Você pode ocultar detalhes de implementação do backend da rede pública.

Um proxy reverso básico para uma aplicação rodando em 127.0.0.1:3000 se parece com isso:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
    }
}

A diretiva proxy_pass diz ao Nginx para onde enviar a requisição. As linhas proxy_set_header preservam o contexto útil da requisição. Sem elas, sua aplicação pode registrar cada requisição como vindo do Nginx e pode não saber se a requisição original usou HTTP ou HTTPS.

Se você é novo na estrutura de host virtual, revise blocos de servidor Nginx antes de dividir o tráfego entre múltiplos domínios.

Roteando Tráfego por Host ou Caminho

As regras de proxy reverso geralmente roteiam por nome de host, caminho ou ambos. O roteamento baseado em host é comum quando aplicações separadas usam domínios diferentes:

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:4000;
        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;
    }
}

O roteamento baseado em caminho é útil quando um domínio atende a vários serviços:

server {
    listen 80;
    server_name example.com;

    location /api/ {
        proxy_pass http://127.0.0.1:4000/;
    }

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

Tenha cuidado com barras no final em proxy_pass. No Nginx, proxy_pass http://backend; e proxy_pass http://backend/; podem reescrever o URI encaminhado de forma diferente quando usados dentro de um bloco de localização. Teste os caminhos de URL exatos que sua aplicação espera.

Por exemplo, se /api/users inesperadamente chegar ao seu backend como /users ou /api/api/users, verifique primeiro a combinação do prefixo de localização e da barra no final. Esse é um dos erros mais comuns de proxy reverso.

Cabeçalhos, Timeouts e WebSockets

Os cabeçalhos tornam o backend ciente da requisição original. O cabeçalho Host é importante quando a aplicação constrói URLs absolutas, valida hosts permitidos ou suporta múltiplos inquilinos. X-Forwarded-For ajuda a preservar o IP do cliente original. X-Forwarded-Proto ajuda as aplicações a gerar links seguros após a terminação TLS.

Se seu backend usa WebSockets, adicione cabeçalhos de upgrade:

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 "upgrade";
    proxy_set_header Host $host;
}

Os timeouts devem corresponder ao comportamento da sua aplicação. Uma requisição web normal deve terminar rapidamente. Uma exportação de relatório, endpoint de streaming ou requisição de long polling pode precisar de mais tempo:

proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

Evite definir timeouts enormes em todos os lugares apenas para esconder um endpoint lento. Timeouts longos podem consumir recursos e tornar as falhas reais mais difíceis de perceber. Ajuste a localização que precisa disso.

O buffering é outra configuração importante. Por padrão, o Nginx pode armazenar em buffer as respostas upstream antes de enviá-las ao cliente. Isso é útil para muitas aplicações web, mas endpoints de streaming podem precisar de buffering desabilitado:

proxy_buffering off;

Use isso apenas onde o comportamento de streaming é necessário. Para respostas HTML e API padrão, o buffering geralmente melhora a estabilidade.

Terminação TLS e Redirecionamentos HTTPS

Em muitas configurações, o Nginx também lida com HTTPS. Isso permite que a aplicação backend rode em uma porta HTTP privada enquanto os usuários obtêm um site seguro normal na porta 443.

Uma forma comum se parece com isso:

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
    }
}

O bloco do servidor de redirecionamento é intencionalmente pequeno. Ele faz um trabalho: mover o tráfego HTTP simples para HTTPS. O bloco do servidor HTTPS lida com o proxy.

Se sua aplicação estiver atrás do Nginx e ainda gerar links http://, verifique se ela confia em X-Forwarded-Proto. Muitos frameworks precisam de uma configuração como "trust proxy" ou uma lista de proxies permitidos antes de usar cabeçalhos encaminhados. Não confie cegamente em cabeçalhos encaminhados da internet pública na camada de aplicação; certifique-se de que apenas o Nginx possa alcançar a porta da aplicação.

Grupos Upstream e Balanceamento de Carga Simples

Quando um backend não é suficiente, defina um grupo upstream:

upstream app_backend {
    server 10.0.1.10:3000;
    server 10.0.1.11:3000;
    keepalive 32;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        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;
    }
}

O Nginx de código aberto usa balanceamento de carga round-robin por padrão. Você também pode usar opções como least_conn quando requisições longas tornam um backend mais ocupado que os outros. A verificação de saúde no Nginx de código aberto é principalmente passiva: se um backend falhar, o Nginx pode marcá-lo como indisponível por um período com base nas configurações de falha. O Nginx Plus tem verificações de saúde ativas, mas não presuma que esses recursos existem em todas as instalações.

Keepalive no bloco upstream mantém as conexões do backend abertas para reutilização. Isso ajuda com muitas requisições pequenas, mas o backend deve ser capaz de lidar com o número de conexões ociosas e ativas que o Nginx pode manter.

Contêineres e Redes Privadas

A configuração de proxy reverso geralmente se torna confusa em Docker ou Kubernetes porque localhost muda de significado. Se o Nginx estiver rodando dentro de um contêiner, 127.0.0.1:3000 aponta para o próprio contêiner do Nginx, não para um contêiner de aplicação separado.

No Docker Compose, faça proxy para o nome do serviço:

location / {
    proxy_pass http://app:3000;
}

No Kubernetes, você geralmente faz proxy para um nome DNS de Serviço, embora muitas implantações Kubernetes usem um controlador Ingress em vez de blocos de servidor Nginx escritos à mão.

A regra simples é esta: teste a conectividade de onde o Nginx roda, não do seu laptop e não do contêiner backend. Se isso falhar, o Nginx também falhará:

curl -v http://app:3000/

Execute isso dentro do contêiner Nginx ou no host Nginx, dependendo da sua implantação.

Limites de Segurança que Vale a Pena Verificar

Um proxy reverso deve reduzir a exposição pública, não criar acidentalmente mais dela. A aplicação backend normalmente deve ouvir em uma interface privada, uma sub-rede privada ou uma rede de contêineres. Se sua aplicação ouvir em 0.0.0.0:3000 em uma VM pública, os usuários podem ser capazes de contornar o Nginx completamente visitando http://example.com:3000.

Verifique as portas de escuta no host:

sudo ss -ltnp

Se o backend precisar ouvir em todas as interfaces dentro de um contêiner, use regras de firewall, grupos de segurança ou configurações de rede do contêiner para que apenas o Nginx possa alcançá-lo de fora. Isso é importante porque as aplicações geralmente dependem do Nginx para TLS, limites de tamanho de requisição, limites de taxa, gateways de autenticação ou listas de permissão de IP.

Também tenha cuidado com cabeçalhos encaminhados. Cabeçalhos como X-Forwarded-For são fáceis de serem falsificados pelos clientes, a menos que o Nginx os sobrescreva e a aplicação confie apenas no proxy. O padrão comum do Nginx é:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Isso anexa o endereço do cliente à cadeia. Sua aplicação ou pipeline de registro deve saber quais endereços de proxy são confiáveis. Caso contrário, a limitação de taxa ou os logs de auditoria podem registrar o IP "cliente" errado.

Os limites de tamanho de requisição também fazem parte desta conversa. Se sua aplicação aceita uploads de arquivos, defina client_max_body_size intencionalmente:

client_max_body_size 25m;

Não aumente globalmente para um valor enorme, a menos que todas as rotas precisem disso. Um endpoint de upload de foto de perfil e um endpoint de login JSON não devem precisar do mesmo limite de corpo de requisição.

Uma Lista de Verificação Prática para Implantação

Antes de considerar o proxy reverso concluído, teste-o como um usuário e como um operador:

  • curl -I http://example.com/ deve mostrar o redirecionamento ou resposta esperados.
  • curl -I https://example.com/ deve mostrar o status e cabeçalhos esperados.
  • Os logs da aplicação devem mostrar o host original e um IP de cliente útil.
  • Endpoints WebSocket ou streaming devem ser testados separadamente.
  • Um caminho errado, como /api/does-not-exist, deve falhar da maneira que sua aplicação espera.
  • Os logs de erro do Nginx devem estar silenciosos durante requisições normais.

Para roteamento de caminho, gosto de testar três URLs para cada localização: o prefixo simples, um caminho aninhado normal e um caminho com uma string de consulta. Por exemplo:

curl -i http://example.com/api/
curl -i http://example.com/api/users
curl -i 'http://example.com/api/users?page=2'

Essas verificações simples detectam muitos erros de barra no final antes dos usuários.

Ao recarregar, use a mesma sequência segura sempre:

sudo nginx -t
sudo systemctl reload nginx
sudo tail -n 50 /var/log/nginx/error.log

Se a aplicação estiver atrás de terminação TLS, verifique também se os links gerados, redirecionamentos, cookies e URLs de retorno de chamada usam HTTPS. Os fluxos de login são onde isso geralmente quebra primeiro, porque redirecionamentos e cookies seguros dependem da aplicação entender o esquema original.

Padrões Comuns de Falha

502 Bad Gateway geralmente significa que o Nginx alcançou a localização do proxy reverso, mas não conseguiu obter uma resposta válida do upstream. O backend pode estar inativo, a porta pode estar errada, a aplicação pode estar ouvindo em uma interface diferente ou a conexão pode ser recusada por um firewall.

504 Gateway Timeout geralmente significa que o Nginx se conectou a algo, mas não recebeu uma resposta a tempo. Isso pode ser uma aplicação lenta, uma consulta de banco de dados bloqueada, um pool de workers sobrecarregado ou um timeout muito curto para o endpoint. Aumentar proxy_read_timeout pode ser apropriado para um endpoint de exportação de longa duração conhecido. Não é uma correção para uma aplicação geralmente lenta.

Loops de redirecionamento geralmente vêm de uma incompatibilidade entre a terminação TLS e as configurações de confiança da aplicação. O navegador alcança o Nginx via HTTPS, o Nginx faz proxy para a aplicação via HTTP, e a aplicação pensa que a requisição original era HTTP simples. A aplicação redireciona para HTTPS, mas a mesma coisa acontece novamente. Passar X-Forwarded-Proto é apenas metade da correção; a aplicação também deve confiar nele do proxy.

IPs de cliente ausentes geralmente aparecem como cada requisição vindo de 127.0.0.1, um endereço de ponte Docker ou um endereço de balanceador de carga privado. Passe X-Real-IP e X-Forwarded-For, depois configure a aplicação e a camada de registro para lê-los com segurança.

Ativos estáticos quebrados após o roteamento de caminho geralmente vêm de aplicações que assumem que vivem em /. Se você montar uma aplicação em /admin/, ela ainda pode gerar links para /assets/app.css. Às vezes, você pode corrigir isso com configurações de caminho base da aplicação. Tentar reescrever cada caminho de ativo no Nginx geralmente é frágil.

Um Pequeno Exemplo do Mundo Real

Imagine uma VM executando três serviços:

  • Um site de marketing em 127.0.0.1:3000
  • Uma API em 127.0.0.1:4000
  • Uma ferramenta de administração em 127.0.0.1:5000

Você pode roteá-los assim:

server {
    listen 443 ssl;
    server_name example.com;

    location /api/ {
        proxy_pass http://127.0.0.1:4000/;
        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;
    }

    location /admin/ {
        proxy_pass http://127.0.0.1:5000/;
        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;
    }

    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;
    }
}

Isso pode funcionar, mas tem desvantagens. A API e a aplicação de administração devem ambas se comportar corretamente sob seus prefixos. Se não o fizerem, nomes de host separados como api.example.com e admin.example.com podem ser mais limpos. Um bom design de proxy reverso não é apenas sobre fazer o Nginx aceitar a configuração; é sobre escolher o roteamento com o qual suas aplicações possam conviver.

Testando e Solucionando Problemas da Configuração

Sempre teste a configuração antes de recarregar:

nginx -t

Depois recarregue o Nginx e faça uma requisição através do nome de host público. Verifique tanto o navegador quanto os logs. Os logs de acesso do Nginx mostram se a requisição alcançou o Nginx. Os logs de erro mostram falhas de conexão, timeouts upstream e detalhes de bad gateway.

Um exemplo prático: sua aplicação Node.js funciona bem em curl http://127.0.0.1:3000, mas o site público mostra 502 Bad Gateway. Isso significa que o Nginx está acessível, mas não consegue falar com o upstream com sucesso. Verifique se a aplicação está ouvindo no endereço esperado, se a porta está correta e se um firewall local bloqueia a conexão.

Problemas comuns de proxy reverso incluem:

  • Porta ou endereço upstream errado.
  • Backend vinculado a localhost quando o Nginx roda em outro contêiner.
  • Cabeçalhos de upgrade WebSocket ausentes.
  • Aplicação rejeitando requisições porque o cabeçalho Host é inesperado.
  • Reescrevitura de URI incorreta causada por uma barra no final.
  • Timeouts muito curtos para um endpoint lento.

Para falhas upstream mais profundas, use Solução de problemas Nginx 502.

Quando Pedir Ajuda

Peça ajuda a um engenheiro DevOps se o proxy reverso abranger vários contêineres, redes privadas, certificados TLS ou upstreams com balanceamento de carga. Essas configurações podem falhar de maneiras que parecem problemas do Nginx, mas são na verdade problemas de DNS, firewall, rede de contêineres ou saúde da aplicação.

Você também deve pedir ajuda antes de expor painéis de administração, APIs internas ou serviços de staging através de um proxy reverso público. Pequenos erros de roteamento podem criar sérios problemas de acesso.

Uma configuração de proxy reverso Nginx é um dos padrões mais úteis na infraestrutura web. Mantenha o roteamento claro, passe os cabeçalhos corretos, teste o comportamento do caminho cuidadosamente e deixe o Nginx ser o ponto de entrada público estável para seus serviços backend.