Reverse proxy com Nginx: encaminhando para aplicações e serviços

Capítulo 7

Tempo estimado de leitura: 9 minutos

+ Exercício

O que é reverse proxy (e por que usar)

Quando uma aplicação (Node.js, Python, Java etc.) roda em uma porta interna (por exemplo, 127.0.0.1:3000), você pode colocar o Nginx na frente dela para receber as requisições HTTP/HTTPS do cliente e encaminhar para a aplicação. Nesse papel, o Nginx atua como reverse proxy: o cliente conversa com o Nginx, e o Nginx conversa com o serviço interno.

Na prática, isso permite: padronizar a porta pública (80/443), centralizar TLS (HTTPS), aplicar limites e timeouts, registrar logs consistentes, e até rotear múltiplas aplicações por domínio/caminho sem expor portas internas.

Encaminhando requisições com proxy_pass

A diretiva central do reverse proxy no Nginx é proxy_pass. Ela define o upstream (destino) para onde o Nginx vai encaminhar a requisição.

Exemplo mínimo: proxy para um app em localhost

server {    listen 80;    server_name app.exemplo.local;    location / {        proxy_pass http://127.0.0.1:3000;    }}

Esse exemplo já encaminha as requisições para a aplicação, mas ainda faltam headers importantes e ajustes comuns (timeouts, buffering, WebSocket etc.).

proxy_pass com e sem barra final: diferença prática

A presença (ou ausência) da barra final em proxy_pass muda como o Nginx monta a URI repassada ao upstream. Isso é uma fonte comum de bugs, principalmente quando você publica uma aplicação em um subcaminho (ex.: /api/).

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

Caso A: proxy_pass sem barra final

Quando você usa proxy_pass http://127.0.0.1:3000 (sem a barra final), o Nginx tende a repassar a URI original como está.

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

Requisição do cliente: GET /api/users

Upstream recebe: GET /api/users

Isso é útil quando sua aplicação já espera o prefixo /api nas rotas.

Caso B: proxy_pass com barra final

Quando você usa proxy_pass http://127.0.0.1:3000/ (com barra final), o Nginx substitui o prefixo que casou no location pelo caminho do proxy_pass. Na prática, isso costuma remover o prefixo do location ao encaminhar.

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

Requisição do cliente: GET /api/users

Upstream recebe: GET /users

Isso é útil quando você quer expor /api publicamente, mas sua aplicação interna foi construída para servir em / (sem o prefixo).

Regra mental rápida

  • Sem barra final em proxy_pass: normalmente preserva a URI como o cliente enviou.
  • Com barra final em proxy_pass: normalmente reescreve/remapeia o caminho, removendo o prefixo do location (quando o location é por prefixo, como /api/).

Se você notar 404 no upstream ao publicar em subcaminho, verifique primeiro essa diferença.

Headers essenciais ao fazer proxy

Ao atuar como reverse proxy, o Nginx vira o “cliente” do seu backend. Se você não repassar alguns headers, a aplicação pode perder informações importantes: host original, IP real do usuário, e se a conexão externa era HTTP ou HTTPS.

Conjunto recomendado de headers

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;}
  • Host: preserva o host solicitado pelo cliente (útil para apps multi-tenant, geração de links, validações de domínio).
  • X-Real-IP: envia o IP do cliente visto pelo Nginx.
  • X-Forwarded-For: cadeia de IPs (cliente + proxies intermediários). Use $proxy_add_x_forwarded_for para anexar sem sobrescrever.
  • X-Forwarded-Proto: indica se o cliente acessou via http ou https. Fundamental quando o TLS termina no Nginx e o backend recebe HTTP interno.

Observação: frameworks costumam ter configuração para “confiar” nesses headers (por exemplo, para registrar IP real ou montar URLs corretas). Se o app não estiver configurado para confiar no proxy, ele pode ignorar esses valores.

Timeouts básicos de proxy (evitando travamentos e 504)

Timeouts controlam quanto tempo o Nginx espera em diferentes fases da comunicação com o backend. Ajustes simples evitam conexões presas e ajudam a diagnosticar backends lentos.

Timeouts comuns

location / {    proxy_pass http://127.0.0.1:3000;    proxy_connect_timeout 5s;    proxy_send_timeout 30s;    proxy_read_timeout 30s;}
  • proxy_connect_timeout: tempo máximo para estabelecer conexão TCP com o backend.
  • proxy_send_timeout: tempo máximo para o Nginx enviar a requisição ao backend (útil se o backend estiver travando ao receber).
  • proxy_read_timeout: tempo máximo esperando o backend responder (muito relevante para endpoints lentos).

Se você tem endpoints longos (relatórios, exportações), aumente proxy_read_timeout para esses caminhos específicos, em vez de aumentar globalmente.

Buffering de proxy: conceito e quando ajustar

O Nginx pode fazer buffering da resposta do backend: ele lê a resposta, armazena em buffers (memória e, dependendo do tamanho, disco), e então envia ao cliente. Isso pode melhorar desempenho e proteger o backend contra clientes lentos (slow clients), porque o backend termina de responder mais rápido para o Nginx.

Por outro lado, para respostas em streaming (SSE), alguns tipos de long-polling, ou quando você quer baixa latência “byte a byte”, o buffering pode atrapalhar, pois o Nginx pode segurar dados antes de repassar.

Diretivas conceituais mais usadas

location / {    proxy_pass http://127.0.0.1:3000;    proxy_buffering on;    proxy_buffers 8 16k;    proxy_buffer_size 16k;}

Para casos de streaming, você pode desabilitar:

location /stream/ {    proxy_pass http://127.0.0.1:3000;    proxy_buffering off;}

Use proxy_buffering off apenas onde fizer sentido, pois ele pode aumentar a pressão no backend quando muitos clientes são lentos.

Exemplo completo de reverse proxy (com headers e timeouts)

server {    listen 80;    server_name app.exemplo.local;    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;        proxy_connect_timeout 5s;        proxy_send_timeout 30s;        proxy_read_timeout 30s;        proxy_buffering on;    }}

Esse é um “padrão seguro” para começar. A partir daí, você ajusta por rota (por exemplo, /api/ com timeout maior, /stream/ sem buffering).

Laboratório: subir uma aplicação simples, publicar via Nginx e validar rotas

Objetivo

Você vai: (1) subir um app HTTP local na porta 3000, (2) configurar o Nginx como reverse proxy, (3) validar headers e rotas, e (4) testar o efeito da barra final no proxy_pass.

1) Criar uma aplicação simples (Node.js)

Crie uma pasta e um arquivo server.js:

const http = require('http');const server = http.createServer((req, res) => {  if (req.url === '/health') {    res.writeHead(200, { 'Content-Type': 'application/json' });    return res.end(JSON.stringify({ ok: true }));  }  if (req.url.startsWith('/api/')) {    res.writeHead(200, { 'Content-Type': 'application/json' });    return res.end(JSON.stringify({      route: req.url,      host: req.headers['host'],      xRealIp: req.headers['x-real-ip'],      xForwardedFor: req.headers['x-forwarded-for'],      xForwardedProto: req.headers['x-forwarded-proto']    }));  }  res.writeHead(200, { 'Content-Type': 'text/plain' });  res.end('Hello from backend on :3000\n');});server.listen(3000, '127.0.0.1', () => {  console.log('Backend listening on http://127.0.0.1:3000');});

Suba o backend:

node server.js

Teste direto (sem Nginx):

curl -i http://127.0.0.1:3000/healthcurl -i http://127.0.0.1:3000/api/test

2) Criar a configuração do Nginx para publicar o app

Crie um arquivo de site (o caminho varia por distro, mas o conteúdo é o importante). Exemplo de conteúdo:

server {    listen 80;    server_name app.exemplo.local;    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;        proxy_connect_timeout 5s;        proxy_send_timeout 30s;        proxy_read_timeout 30s;        proxy_buffering on;    }}

Valide a sintaxe e recarregue o Nginx:

sudo nginx -tsudo nginx -s reload

Se você não tiver DNS para app.exemplo.local, você pode testar com curl enviando o Host:

curl -i -H 'Host: app.exemplo.local' http://127.0.0.1/healthcurl -i -H 'Host: app.exemplo.local' http://127.0.0.1/api/test

3) Validar que os headers chegaram no backend

A rota /api/test devolve os headers recebidos. Verifique se aparecem host, x-real-ip, x-forwarded-for e x-forwarded-proto no JSON.

Para simular um IP anterior na cadeia (como se houvesse outro proxy antes do Nginx), envie um X-Forwarded-For manualmente e veja se o Nginx anexou:

curl -s -H 'Host: app.exemplo.local' -H 'X-Forwarded-For: 203.0.113.10' http://127.0.0.1/api/test

O backend deve receber algo como 203.0.113.10, <ip_do_cliente>.

4) Testar na prática a diferença do proxy_pass com e sem barra final

Agora publique o backend em um subcaminho, por exemplo /backend/, e observe como a URL chega ao app.

Teste 1 (sem barra final):

server {    listen 80;    server_name app.exemplo.local;    location /backend/ {        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;    }}

Recarregue e teste:

sudo nginx -t && sudo nginx -s reloadcurl -s -H 'Host: app.exemplo.local' http://127.0.0.1/backend/api/test

Você deve ver no JSON a rota como /backend/api/test (o prefixo foi preservado).

Teste 2 (com barra final):

server {    listen 80;    server_name app.exemplo.local;    location /backend/ {        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;    }}

Recarregue e teste novamente:

sudo nginx -t && sudo nginx -s reloadcurl -s -H 'Host: app.exemplo.local' http://127.0.0.1/backend/api/test

Agora a rota tende a chegar como /api/test (o prefixo /backend/ foi removido no encaminhamento). Esse é o comportamento desejado quando você quer “montar” o app interno em um caminho público diferente.

5) Ajustar timeouts por rota (exercício rápido)

Se você tivesse uma rota lenta, você poderia aumentar apenas nela:

location /api/relatorio/ {    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;    proxy_read_timeout 120s;}

O restante do site continua com timeouts menores, evitando conexões abertas por muito tempo sem necessidade.

Agora responda o exercício sobre o conteúdo:

Ao publicar um backend interno em um subcaminho (por exemplo, location /backend/), qual é o efeito prático de usar proxy_pass com barra final no encaminhamento da URI?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Com barra final em proxy_pass, o Nginx tende a substituir o prefixo do location pelo caminho do upstream, removendo o subcaminho no repasse (ex.: /backend/api/test/api/test).

Próximo capitúlo

Logs no Nginx: access_log, error_log, formatos e diagnóstico

Arrow Right Icon
Capa do Ebook gratuito Nginx para Iniciantes: Servidor Web, Reverse Proxy e Balanceamento Básico
54%

Nginx para Iniciantes: Servidor Web, Reverse Proxy e Balanceamento Básico

Novo curso

13 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.