O que fica entre o navegador e sua app
Aponte o navegador para example.com e a requisição nunca toca diretamente no processo que executa seu código. Ela encontra primeiro outra coisa: um servidor que lê a requisição, decide qual backend deve responder, a encaminha para lá e devolve a resposta como se a tivesse produzido ele mesmo. Esse servidor é um reverse proxy.
Nginx, Caddy, Traefik e HAProxy fazem esse trabalho, assim como o load balancer que fica na frente de um cluster Kubernetes. O esquema nunca muda: os clientes falam com o proxy e com mais nada. Os servidores reais ficam atrás, em qualquer quantidade e onde quer que rodem.
Como um reverse proxy trata uma requisição
Um cliente abre uma conexão com o IP e a porta do reverse proxy, do mesmo jeito que abriria com qualquer servidor. O proxy termina essa conexão, lê a linha da requisição e os headers, e os compara com suas próprias regras — geralmente o header Host e o caminho da URL. Com base nesse resultado, ele abre uma segunda conexão, separada, com um backend, encaminha a requisição por ela, espera a resposta e a repassa de volta pela conexão original do cliente.
Duas conexões independentes, costuradas uma à outra. Do lado do cliente, isso é indistinguível de falar diretamente com o backend. Do lado do backend, cada requisição parece vir de uma única máquina, o proxy, em vez de vir da internet como um todo.
| |
Como o proxy vê cada requisição antes do backend, ele pode inspecionar, modificar, cachear ou rejeitar qualquer coisa que passe por ali. É essa posição que faz o proxy herdar o trabalho que ninguém quer dentro da aplicação: encerrar o TLS, comprimir respostas, cachear conteúdo estático, limitar clientes abusivos e direcionar caminhos diferentes para serviços diferentes — /api para um backend, / para outro, /static direto do disco.
Reverse proxy vs forward proxy
Os dois ficam no meio de uma conexão. A diferença está em para quem eles trabalham.
| Forward proxy | Reverse proxy | |
|---|---|---|
| Age em nome de | O cliente | O servidor |
| O cliente sabe que existe | Geralmente sim (configurado explicitamente) | Não (parece o servidor real) |
| O que ele esconde | A identidade do cliente do servidor | A identidade do servidor do cliente |
| Uso típico | Filtragem de saída corporativa, contornar geobloqueios, navegação anônima | Roteamento, terminação TLS, cache, proteção dos servidores de origem |
Um forward proxy é a caixa que sua empresa coloca no escritório para que todo o tráfego de saída passe por um único ponto filtrado: o site que você visita registra o endereço do proxy, não o do seu notebook. Um reverse proxy é operado por quem é dono do site, e esconde a outra ponta. De fora, não dá para saber se example.com é um servidor ou quarenta.
Reverse proxy vs load balancer
Os dois termos são usados como sinônimos, principalmente porque um mesmo processo Nginx ou HAProxy costuma fazer os dois trabalhos ao mesmo tempo.
| Reverse proxy | Load balancer | |
|---|---|---|
| Pergunta central que responde | O que deve acontecer com esta requisição? | Qual servidor deve tratar esta requisição? |
| Funciona com um único backend? | Sim, continua útil (TLS, cache, roteamento por caminho) | Não, precisa de pelo menos dois para balancear |
| Recursos extras típicos | Cache, reescrita de headers, terminação TLS, roteamento por caminho | Health checks, distribuição ponderada, afinidade de sessão |
Um reverse proxy na frente de um único backend ainda se justifica: ele termina o TLS para que sua app não precise fazer isso, esconde o endereço real do backend, adiciona gzip. Um load balancer não tem nada a fazer enquanto não houver pelo menos dois backends para escolher. O rótulo que você usa descreve a configuração que você escreveu, não o software que instalou.
Como configurar o Nginx como reverse proxy
Escutar em uma porta, comparar um hostname, encaminhar para um backend. Isso já é o mínimo necessário:
| |
proxy_pass é a linha que importa: tudo o que location captura vai para http://127.0.0.1:3000. As quatro linhas proxy_set_header existem porque, sem elas, o backend vê o proxy como cliente, e cada requisição parece vir de 127.0.0.1. X-Real-IP e X-Forwarded-For carregam o endereço real do cliente até o fim. X-Forwarded-Proto diz ao backend se a requisição original era HTTPS, já que o TLS foi terminado no proxy e o próximo salto é HTTP puro.
Um detalhe quebra configurações o tempo todo: uma barra final na URI do proxy_pass muda o caminho encaminhado. proxy_pass http://backend; (sem caminho) encaminha a URI original sem alterações. proxy_pass http://backend/; (com barra final) remove, antes de encaminhar, a parte da URI que location capturou. Com location /api/ e proxy_pass http://backend/;, uma requisição para /api/users chega ao backend como /users. Inverta isso e todas as rotas retornam 404 no backend, mesmo que um curl direto no proxy pareça funcionar normalmente.
Reverse proxy na frente de uma app em Docker
É aqui que esse padrão aparece mais no dia a dia: Nginx em um container, sua app em outro, ambos na mesma rede Docker para que o Nginx alcance a app pelo nome do container — o comportamento de DNS descrito em o que uma rede Docker oferece.
| |
| |
app expõe a porta 3000 na rede do Compose, mas não publica nada no host. Só o Nginx faz isso, na porta 80. O nome app:3000 na configuração do Nginx é resolvido pelo DNS interno do Docker, o mesmo mecanismo descrito no guia do Docker Compose. Rode docker compose up e as requisições para localhost chegam ao Nginx, que as encaminha para o container da app. A app nunca fica acessível sozinha de fora do host, o que é tão intencional quanto o próprio roteamento.
O que um reverse proxy custa
Ele adiciona um salto de rede e mais um lugar onde uma requisição pode falhar. Um proxy_pass mal configurado ou um header perdido aparece como bug na app quando a falha está uma camada acima. Também é um ponto único de falha por padrão: derrube esse único processo Nginx e cada backend atrás dele fica inacessível. Ambientes de produção ou o rodam de forma redundante, ou passam a tarefa para algo que já assume a falha como certa — um load balancer na nuvem, ou um Ingress controller do Kubernetes apoiado nos Deployments e Pods por trás dele.
Um reverse proxy também não torna uma app mais rápida ou mais escalável sozinho. Ele pode cachear e comprimir, mas um backend lento continua lento; o proxy só encaminha essa lentidão um salto depois. E não é autenticação nem validação de entrada. Bloquear requisições claramente malformadas na borda é um bônus, nunca um motivo para a app parar de checar o que recebe.
Levando essa configuração para produção
Pegue o compose.yaml acima, troque app por um serviço real, e adicione ssl_certificate e ssl_certificate_key ao bloco server do Nginx assim que tiver um certificado. Essa é a diferença entre um exercício local e algo que você colocaria na frente de tráfego real. Se você rotear para mais de um backend, troque o endereço único por um bloco upstream com várias linhas server e aponte o proxy_pass para esse bloco: a mesma configuração vira um load balancer.