Caddy 2026: Reverse Proxy Automático com HTTPS para PME

O Caddy é o único servidor web que obtém e renova certificados TLS automaticamente via Let’s Encrypt sem configuração manual. Para uma PME portuguesa que precisa de expor vários serviços internos (Grafana, Nextcloud, API própria) num único servidor com HTTPS válido, o Caddy reduz a complexidade de Nginx + Certbot para um ficheiro de 10 linhas. Este artigo mostra como configurar reverse proxy, múltiplos domínios, rate limiting e boas práticas de segurança — tudo com configuração copy-paste (Caddy docs).

Neste artigo

  1. Entender o Caddy e o Reverse Proxy
  2. Pré-requisitos e Instalação
  3. Passo 1 — Configurar Caddy como Reverse Proxy Simples
  4. Passo 2 — Múltiplos Domínios e Subdomínios
  5. Passo 3 — HTTPS Automático com Let’s Encrypt
  6. Passo 4 — Rate Limiting e Protecção contra Abuso
  7. Passo 5 — Headers de Segurança e CORS
  8. Passo 6 — Logging e Monitorização
  9. Caddy vs Nginx — Quando Escolher Qual
  10. Erros Comuns
  11. Checklist Rápido de Verificação

1. Entender o Caddy e o Reverse Proxy

Um reverse proxy é um intermediário entre o utilizador (browser) e os serviços internos. Em vez de cada serviço ter a sua própria porta exposta (porta 3000 para Grafana, porta 8080 para API, porta 9000 para PHP), o reverse proxy recebe todo o tráfego na porta 443 (HTTPS) e encaminha para o serviço correcto baseado no domínio.

Analogia: um reverse proxy é como um porteiro de um edifício de escritórios. O visitante chega à porta principal (porta 443), diz para quem vai (“grafana.empresa.pt”), e o porteiro encaminha-o para a sala certa (porta 3000) sem o visitante saber onde fica a sala.

O Caddy diferencia-se do Nginx por automatizar completamente os certificados TLS. Com Nginx precisas de Certbot + cron + renovação manual. Com Caddy, escreves grafana.empresa.pt no ficheiro de configuração e ele obtém o certificado automaticamente (Caddy automatic HTTPS).

Feature Caddy 2 Nginx + Certbot Traefik
HTTPS automático Nativo (Let’s Encrypt + ZeroSSL) Requer Certbot + cron Nativo
Configuração Caddyfile (simples) nginx.conf (complexo) YAML/TOML (médio)
Performance Alta (Go) Muito alta (C) Alta (Go)
API dinâmica Sim (admin API) Não (reload manual) Sim
Tamanho binário ~30 MB ~5 MB ~100 MB
Ideal para PME, simples, HTTPS auto Alto tráfego, tuning fino Docker/K8s

2. Pré-requisitos e Instalação

Requisito Detalhe
Servidor Linux Ubuntu 22.04+, Debian 12+, RHEL 9+
IP público Necessário para HTTPS automático (Let’s Encrypt valida via HTTP-01)
Domínio configurado Registo A a apontar para o IP do servidor
Portas 80 + 443 abertas Let’s Encrypt usa a porta 80 para validação
RAM mínima 256 MB (Caddy é leve)

Instalar o Caddy nas principais distribuições:

Ubuntu/Debian:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf ‘https://dl.cloudsmith.io/public/caddy/stable/gpg.key’ | sudo gpg –dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf ‘https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt’ | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

O que cada linha faz:

  • debian-keyring + apt-transport-https — pré-requisitos para repositórios seguros
  • curl ... gpg.key — adiciona a chave GPG do repositório Caddy para verificar autenticidade dos pacotes
  • curl ... debian.deb.txt — adiciona o repositório Caddy à lista de fontes do apt
  • apt install caddy — instala o Caddy e regista o serviço systemd

Fedora/RHEL:

sudo dnf install ‘dnf-command(copr)’
sudo dnf copr enable @caddy/caddy
sudo dnf install caddy

Arch Linux:

sudo pacman -S caddy

ℹ️ Nota: Se não tiveres curl instalado, usar wget -qO- URL como alternativa. Em sistemas minimalistas, apt install curl primeiro.

3. Passo 1 — Configurar Caddy como Reverse Proxy Simples

O Caddyfile é o ficheiro de configuração principal do Caddy. Fica em /etc/caddy/Caddyfile (Linux). A sintaxe é deliberadamente simples — cada bloco é um domínio.

Cenário: uma PME tem o Grafana a correr na porta 3000 e quer expô-lo em grafana.empresa.pt com HTTPS automático.

sudo tee /etc/caddy/Caddyfile > /dev/null << 'EOF' grafana.empresa.pt { reverse_proxy localhost:3000 } EOF

O que cada linha faz:

  • grafana.empresa.pt — domínio que o Caddy vai servir. Ao detectar um domínio válido, o Caddy obtém automaticamente o certificado TLS
  • reverse_proxy localhost:3000 — encaminha todo o tráfego recebido para o Grafana na porta 3000

Reiniciar o Caddy para aplicar a configuração:

sudo systemctl restart caddy
sudo systemctl status caddy

Depois de reiniciar, abrir https://grafana.empresa.pt no browser. O Caddy já obteve o certificado e o Grafana está acessível com HTTPS válido — sem Certbot, sem cron, sem configuração manual de certificados.

4. Passo 2 — Múltiplos Domínios e Subdomínios

Uma PME típica tem vários serviços. Em vez de um servidor por serviço, o Caddy encaminha múltiplos domínios num único servidor.

sudo tee /etc/caddy/Caddyfile > /dev/null << 'EOF' grafana.empresa.pt { reverse_proxy localhost:3000 } nextcloud.empresa.pt { reverse_proxy localhost:8080 } api.empresa.pt { reverse_proxy localhost:9000 { header_up X-Real-IP {remote_host} header_up X-Forwarded-For {remote_host} } } empresa.pt { reverse_proxy localhost:80 redir /blog/* https://blog.empresa.pt{uri} } EOF

O que cada bloco faz:

  • grafana.empresa.pt — encaminha para Grafana na porta 3000
  • nextcloud.empresa.pt — encaminha para Nextcloud na porta 8080
  • api.empresa.pt — encaminha para a API na porta 9000, injectando headers X-Real-IP e X-Forwarded-For para a API saber o IP real do cliente (não o IP do Caddy)
  • empresa.pt — site principal na porta 80, com redireccionamento de /blog/* para blog.empresa.pt

Recarregar a configuração sem reiniciar o serviço (hot reload):

sudo caddy reload –config /etc/caddy/Caddyfile

O reload aplica a nova configuração sem cortar ligações existentes — útil em produção.

5. Passo 3 — HTTPS Automático com Let’s Encrypt

O Caddy obtém certificados automaticamente quando detecta um domínio válido no Caddyfile. Por defeito usa Let’s Encrypt e ZeroSSL como autoridades certificadoras. Não precisa de Certbot.

Para forçar apenas Let’s Encrypt ou configurar email para notificações de expiração:

sudo tee /etc/caddy/Caddyfile > /dev/null << 'EOF' { admin off email [email protected] } grafana.empresa.pt { reverse_proxy localhost:3000 } EOF

O que cada linha faz:

  • admin off — desliga a API admin do Caddy (boa prática de segurança em produção)
  • email [email protected] — email registado com Let’s Encrypt para receber notificações de expiração
  • O bloco global { } no topo aplica configurações a todos os domínios

⚠️ Atenção

Se o domínio não estiver a apontar para o IP público do servidor, a validação HTTP-01 do Let’s Encrypt vai falhar e o Caddy não obtém o certificado. Verificar com dig grafana.empresa.pt antes de aplicar a configuração.

Usar o modo DNS-01 para validação

Se o servidor não tiver a porta 80 acessível (firewall restritivo), o Caddy pode validar via DNS-01 usando um plugin. Para Cloudflare:

# Compilar Caddy com plugin Cloudflare (não vem por defeito):
xcaddy build –with github.com/caddy-dns/cloudflare

Depois no Caddyfile:

grafana.empresa.pt {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
reverse_proxy localhost:3000
}

O dns cloudflare usa a API da Cloudflare para criar o registo TXT de validação automaticamente. Necessita de um API token da Cloudflare na variável de ambiente CLOUDFLARE_API_TOKEN (Cloudflare API tokens).

6. Passo 4 — Rate Limiting e Protecção contra Abuso

Para proteger serviços backend de sobrecarga, o Caddy suporta rate limiting via módulo nativo (Caddy 2.7+):

api.empresa.pt {
rate_limit {
zone api {
key {remote_host}
events 100
window 1m
}
}
reverse_proxy localhost:9000
}

O que cada parâmetro faz:

  • zone api — nome da zona de rate limiting (pode haver várias zonas)
  • key {remote_host} — limita por IP do cliente. Alternativas: {http.request.header.Authorization} para limitar por token
  • events 100 — máximo 100 pedidos por janela
  • window 1m — janela de 1 minuto. Quando o cliente excede, recebe HTTP 429 (Too Many Requests)

Para bloquear IPs específicos ou path patterns suspeitos:

api.empresa.pt {
@blocked remote_ip 10.0.0.50 10.0.0.51
abort @blocked

@suspicious path /wp-admin* /xmlrpc.php /.env
respond @suspicious 403

reverse_proxy localhost:9000
}

  • @blocked — matcher que identifica IPs bloqueados
  • abort @blocked — fecha a ligação imediatamente (não envia resposta)
  • @suspicious — matcher para paths conhecidos de probes/scanners
  • respond @suspicious 403 — responde com 403 Forbidden

7. Passo 5 — Headers de Segurança e CORS

O Caddy pode injectar headers HTTP de segurança em todas as respostas:

grafana.empresa.pt {
header {
Strict-Transport-Security “max-age=31536000; includeSubDomains; preload”
X-Content-Type-Options “nosniff”
X-Frame-Options “SAMEORIGIN”
Referrer-Policy “strict-origin-when-cross-origin”
Permissions-Policy “geolocation=(), microphone=(), camera=()”
}
reverse_proxy localhost:3000
}

O que cada header faz:

  • Strict-Transport-Security — força o browser a usar HTTPS durante 1 ano. Previne downgrade attacks
  • X-Content-Type-Options nosniff — impede o browser de adivinhar o MIME type (previne XSS via upload de ficheiros)
  • X-Frame-Options SAMEORIGIN — impede que a página seja embebida num iframe de outro domínio (previne clickjacking)
  • Referrer-Policy — controla quanto do Referer header é enviado para sites externos
  • Permissions-Policy — desactiva APIs do browser que a aplicação não usa

Para configurar CORS numa API:

api.empresa.pt {
@cors header Origin api.empresa.pt
header @cors Access-Control-Allow-Origin “https://app.empresa.pt”
header @cors Access-Control-Allow-Methods “GET, POST, PUT, DELETE, OPTIONS”
header @cors Access-Control-Allow-Headers “Authorization, Content-Type”

@options method OPTIONS
respond @options 204

reverse_proxy localhost:9000
}

  • @cors — matcher que detecta o header Origin
  • Access-Control-Allow-Origin — permite apenas o domínio da aplicação frontend
  • respond @options 204 — responde a preflight requests com 204 No Content

8. Passo 6 — Logging e Monitorização

O Caddy pode logar em JSON estruturado para integração com ELK, Loki ou Grafana:

{
logging {
output file /var/log/caddy/access.log {
roll_size 100mb
roll_keep 10
roll_keep_for 720h
}
format json
}
}

grafana.empresa.pt {
log
reverse_proxy localhost:3000
}

O que cada parâmetro faz:

  • output file /var/log/caddy/access.log — escreve logs para ficheiro
  • roll_size 100mb — rota o ficheiro quando atinge 100 MB
  • roll_keep 10 — mantém 10 ficheiros antigos
  • roll_keep_for 720h — mantém ficheiros por 30 dias (720 horas)
  • format json — logs em JSON para fácil parsing
  • log — ativa logging para o domínio (sem argumentos usa a config global)

Para ver logs em tempo real:

sudo journalctl -u caddy -f

Para métricas do Caddy (Prometheus), ativar a API admin (remover admin off) e expor /metrics:

{
admin :2019
}

grafana.empresa.pt {
reverse_proxy localhost:3000
}

metrics.empresa.pt {
reverse_proxy localhost:2019
}

Depois configurar Prometheus para fazer scrape de https://metrics.empresa.pt/metrics (Caddy metrics).

9. Caddy vs Nginx — Quando Escolher Qual

Critério Caddy Nginx
Simplicidade Caddyfile de 5 linhas nginx.conf de 50+ linhas
HTTPS auto Nativo, zero config Requer Certbot + cron
Performance Alta (suficiente para PME) Muito alta (tuning fino)
Comunidade Crescente, menor Enorme, documentação extensa
Módulos Compile-time (xcaddy) Dinâmicos
Ideal para PME, protótipos, HTTPS sem dor Alto tráfego, hosting, tuning
Configuração dinâmica API REST Reload manual
Suporte HTTP/3 Nativo Requer build especial

Para uma PME com 5-10 serviços internos que precisa de HTTPS sem gerir certificados, Caddy é a escolha óbvia. Para um hoster com 1000+ domínios e necessidades de tuning fino de performance, Nginx continua a ser a referência (comparação Caddy vs Nginx).

10. Erros Comuns

Problema Causa Solução
HTTPS não funciona, certificado não obtido Domínio não aponta para o IP público Verificar dig dominio.pt — o registo A tem de apontar para o servidor
Erro “too many redirects” Backend já força HTTPS e o Caddy envia HTTPS Adicionar header_up X-Forwarded-Proto https no reverse_proxy
WebSocket não funciona Caddy não faz upgrade do protocolo por defeito Adicionar reverse_proxy localhost:PORT { header_up Host {host} } — o Caddy 2.6+ faz auto-WebSocket
Porta 80 bloqueada pelo firewall Let’s Encrypt não consegue validar HTTP-01 Usar validação DNS-01 ou abrir porta 80 apenas para validação
Rate limit não funciona Módulo rate_limit não incluído no binário Compilar com xcaddy build --with github.com/caddyserver/caddy-ratelimit
Logs não aparecem log não declarado no bloco do domínio Adicionar log dentro do bloco do domínio (não apenas no global)
Caddy não arranca após reload Erro de sintaxe no Caddyfile Correr caddy validate --config /etc/caddy/Caddyfile antes de reload
Certificado expirado Caddy não consegue renovar (firewall/DNS) Verificar journalctl -u caddy — procurar erros ACME. Renovar manualmente com caddy reload

11. Checklist Rápido de Verificação

  • [ ] Caddy instalado e a correr (systemctl status caddy)
  • [ ] Caddyfile válido (caddy validate --config /etc/caddy/Caddyfile)
  • [ ] Domínios a apontar para o IP público (dig dominio.pt)
  • [ ] Portas 80 e 443 abertas no firewall
  • [ ] HTTPS funciona no browser (cadeado verde)
  • [ ] Headers de segurança presentes (ver com curl -I https://dominio.pt)
  • [ ] Logs a escrever em /var/log/caddy/access.log
  • [ ] Rate limiting configurado em APIs expostas
  • [ ] Reload testado sem cortar ligações (caddy reload)
  • [ ] Reinício do servidor — Caddy arranca automaticamente (systemctl enable caddy)

Artigos Relacionados

← Voltar ao Índice

Este artigo foi útil?

Duarte Spínola

Deixe um Comentário