<DevDiary />
← Voltar para o blog

Nginx ou Caddy: qual colocar na frente da sua aplicação na VPS

Subindo projetos na VPS, precisei decidir quem fica na porta 80/443 cuidando de domínio, HTTPS e proxy. Comparo Nginx e Caddy no mesmo cenário, com configs lado a lado, e deixo um checklist de como escolher.

Nos últimos dias eu estava colocando trabalhos no ar na minha VPS e cheguei naquele ponto que todo mundo chega: a aplicação já roda, o domínio já aponta pro IP, mas falta alguém na frente dela. Alguém que receba a requisição na porta 80/443, cuide do HTTPS e entregue tudo pra aplicação certa.

Os dois nomes que sempre aparecem são Nginx e Caddy. Testei os dois e esta entrada é o resumo do que aprendi, do jeito mais direto que consegui.

Primeiro: o que esse "alguém na frente" faz?

Pensa num prédio comercial com várias empresas. O visitante chega na recepção, diz pra onde vai, e a recepcionista manda ele pro andar certo. Ela também confere quem está entrando.

Na VPS, essa recepção é o reverse proxy. Ele:

  • recebe tudo que chega nas portas 80 (HTTP) e 443 (HTTPS);
  • olha o domínio pedido (api.cliente.com, loja.cliente.com...) e repassa pra aplicação certa, que normalmente roda numa porta interna como localhost:8000;
  • cuida do certificado SSL, pra sua aplicação não precisar se preocupar com isso;
  • pode servir arquivos estáticos, comprimir respostas, colocar cabeçalhos de segurança etc. Tanto o Nginx quanto o Caddy fazem tudo isso. A diferença está em como você chega lá.

Nginx em um minuto

O Nginx é o veterano. Escrito em C, roda em uma quantidade enorme de servidores no mundo e é praticamente o padrão da indústria. Qualquer erro que você encontrar, alguém já encontrou antes e postou a solução.

O ponto que pesa: ele não faz HTTPS sozinho. Você configura o site e depois usa uma ferramenta à parte, normalmente o Certbot, pra pedir o certificado no Let's Encrypt e programar a renovação.

Caddy em um minuto

O Caddy é mais novo, escrito em Go, e a proposta dele é: HTTPS automático por padrão. Você escreve o domínio no arquivo de configuração e pronto. Ele pede o certificado, instala, renova antes de vencer e já redireciona HTTP pra HTTPS. Sem Certbot, sem cron, sem lembrar de nada.

O arquivo de configuração dele, o Caddyfile, também é bem mais curto que o do Nginx.

O mesmo cenário nos dois

Cenário: tenho uma API rodando em localhost:8000 (pode ser Spring Boot, Node, php artisan serve, um container...) e quero que ela responda em https://api.exemplo.com.

Com Nginx

Arquivo em /etc/nginx/sites-available/api.exemplo.com:

server {
    listen 80;
    server_name api.exemplo.com;
 
    location / {
        proxy_pass http://127.0.0.1:8000;
        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;
    }
}

Depois ativa, testa e recarrega:

sudo ln -s /etc/nginx/sites-available/api.exemplo.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

E só agora vem o HTTPS:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d api.exemplo.com

O Certbot edita o seu arquivo, adiciona o bloco da porta 443 e deixa a renovação agendada.

Com Caddy

Arquivo /etc/caddy/Caddyfile:

api.exemplo.com {
    reverse_proxy localhost:8000
}
caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

É isso. HTTPS, renovação e redirecionamento de HTTP pra HTTPS já vêm junto. Os cabeçalhos X-Forwarded-* também são repassados por padrão.

Foi nesse momento que eu entendi por que tanta gente fala do Caddy.

E pra um projeto Laravel com PHP-FPM?

Aqui não tem "porta 8000": o servidor web conversa direto com o PHP-FPM.

Nginx:

server {
    listen 80;
    server_name app.exemplo.com;
    root /var/www/app/public;
    index index.php;
 
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
 
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.4-fpm.sock;
    }
}

(e depois o Certbot, de novo)

Caddy:

app.exemplo.com {
    root * /var/www/app/public
    encode zstd gzip
    php_fastcgi unix//run/php/php8.4-fpm.sock
    file_server
}

O php_fastcgi do Caddy já faz o papel daquele try_files mandando tudo pro index.php.

Pegadinha de Laravel atrás de proxy: se a sua aplicação roda atrás de um proxy (em container, por exemplo) e começa a gerar links http:// ou dar loop de redirecionamento, falta confiar no proxy. No Laravel 11/12 isso fica no bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->trustProxies(at: '*');
})

Lado a lado

NginxCaddy
HTTPSManual, via CertbotAutomático, já vem ligado
Renovação do certificadoAgendada pelo CertbotFeita pelo próprio Caddy
ConfiguraçãoMais verbosa, muito flexívelCurta e legível
HTTP/3Suportado, mas precisa configurarLigado por padrão
Material e exemplos na internetGigantescoBom, mas bem menor
Uso de memóriaMuito baixoBaixo (um pouco maior que o Nginx)
Painéis de hospedagem e tutoriais de empresasQuase sempre assumem NginxRaramente
Curva de aprendizadoMédiaBaixa

Sobre desempenho: pra uma VPS rodando alguns sites e APIs, os dois sobram. Gargalo nesse tamanho de projeto quase sempre é banco de dados ou código, não o proxy.

Como eu escolho agora

Vou de Caddy quando:

  • é uma VPS minha ou de cliente pequeno, com poucos domínios;

  • quero subir rápido e não quero lembrar de certificado nunca mais;

  • o proxy é só proxy: recebe e repassa, sem regra muito exótica;

  • estou usando Docker e quero um docker-compose.yml simples. Vou de Nginx quando:

  • o servidor já tem Nginx (não vale migrar o que funciona só por gosto);

  • o cliente ou a empresa já usa Nginx e outras pessoas vão dar manutenção;

  • preciso de configuração muito específica: cache avançado, regras complexas de rewrite, balanceamento com ajustes finos;

  • vou seguir um tutorial ou documentação oficial de alguma ferramenta que só traz exemplo pra Nginx. Resumindo numa frase: Caddy pra começar rápido e sem dor de cabeça com HTTPS; Nginx quando o contexto exige o padrão da indústria ou controle fino. Nenhuma das duas escolhas é errada.

Bônus: Caddy no Docker Compose

Como na minha VPS quase tudo roda em container, esse foi o formato que mais usei:

services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp" # HTTP/3
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
 
  api:
    image: minha-api:latest
    restart: unless-stopped
 
volumes:
  caddy_data:
  caddy_config:

E no Caddyfile, em vez de localhost, uso o nome do serviço:

api.exemplo.com {
    reverse_proxy api:8000
}

Coisas que dão errado (e aconteceriam com qualquer um dos dois)

  1. O DNS ainda não apontou pro IP da VPS. O Let's Encrypt precisa acessar seu domínio pra emitir o certificado. Se o DNS não propagou, a emissão falha. Confere com dig api.exemplo.com antes.
  2. Portas 80 e 443 fechadas no firewall. Se usa UFW: sudo ufw allow 80,443/tcp (e 443/udp se quiser HTTP/3).
  3. Os dois instalados ao mesmo tempo. Só um programa pode escutar na porta 80. Se o Caddy não sobe, veja se o Nginx não ficou rodando: sudo ss -tlnp | grep ':80'.
  4. Perder o volume /data do Caddy no Docker. É ali que ficam os certificados. Sem volume, a cada recriação do container ele pede tudo de novo, e o Let's Encrypt tem limite de emissões. Por isso o caddy_data no compose acima.
  5. Esquecer de testar a configuração antes de recarregar. nginx -t e caddy validate existem pra isso. Um erro de digitação derruba todos os sites do servidor de uma vez.

O que ficou de lição

Eu achava que a escolha seria por desempenho, e no fim ela é sobre tempo e contexto. Nos meus projetos pessoais e de clientes pequenos, o Caddy me poupa a parte mais chata (certificado) e deixa a configuração tão curta que eu entendo ela de novo daqui a seis meses. Já quando o ambiente é de outra pessoa ou o cenário é mais complexo, o Nginx continua sendo a escolha mais segura justamente por ser o padrão.

Se você está subindo a primeira aplicação numa VPS agora, minha sugestão é: começa com Caddy, entende o que um reverse proxy faz, e aprende Nginx quando aparecer um motivo concreto.