
Colocar um site no ar com HTTPS costumava ser uma receita de três ingredientes: um servidor web, o certbot e um cron para renovar o certificado — e um nginx.conf de cinquenta linhas para amarrar tudo. O Caddy resolve isso numa peça só: você escreve o nome do domínio no arquivo de configuração e ele emite o certificado, renova sozinho e redireciona HTTP para HTTPS. Este guia instala o Caddy no Ubuntu e no Debian pelo repositório oficial, configura site estático, proxy reverso com balanceamento e PHP, e mostra onde ficam os certificados, os logs e a API de administração.
O que é o Caddy
O Caddy é um servidor web e proxy reverso escrito em Go, distribuído como um binário único e licenciado sob Apache 2.0. Matt Holt começou a escrevê-lo em 2014, ainda na faculdade; a primeira versão pública (0.5.0) saiu em abril de 2015, e a linha 2.x, reescrita do zero, chegou em maio de 2020. A versão estável atual é a 2.11.7, de 3 de outubro de 2026.
Três características o separam do nginx e do Apache:
- HTTPS automático e por padrão. Todo site com nome de domínio público ganha certificado de uma CA ACME (Let’s Encrypt ou ZeroSSL) e renovação automática. Nomes locais, como
localhost, recebem certificados de uma CA interna do próprio Caddy. - Protocolos modernos ligados de fábrica. HTTP/2 e, desde a 2.6 (setembro de 2022), HTTP/3. A 2.10, de abril de 2025, acrescentou troca de chaves pós-quântica (
x25519mlkem768) por padrão e suporte a Encrypted ClientHello (ECH). - Configuração por API. O formato nativo é JSON, carregado e alterado ao vivo por uma API REST em
localhost:2019. O Caddyfile, que usaremos aqui, é um adaptador mais legível que vira esse JSON.
O que ele não faz: não tem o ecossistema de módulos dinâmicos do nginx nem o .htaccess do Apache. Plugins entram compilados no binário — veremos como no fim.

Instalando pelo repositório oficial
O projeto mantém pacotes para Debian, Ubuntu e Raspberry Pi OS num repositório hospedado no Cloudsmith. Os comandos abaixo são os da documentação oficial; testei-os num Debian 13 limpo e o pacote instalou a 2.11.7:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
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 chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
Se o sistema não tiver o gpg, instale antes com sudo apt install -y gpg. O pacote faz bastante coisa sozinho:
- cria o usuário de sistema
caddy, com$HOMEem/var/lib/caddye membro do grupowww-data; - instala e já inicia o serviço
caddy.service, que lê/etc/caddy/Caddyfile; - instala também o
caddy-api.service, desativado, para quem prefere configurar só pela API; - deixa um Caddyfile de exemplo servindo
/usr/share/caddyna porta 80.
caddy version
systemctl status caddy --no-pager
curl -I http://localhost
Libere as portas no firewall. O HTTP/3 roda sobre UDP, então a 443 precisa estar aberta nos dois protocolos:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
Primeiro site com HTTPS automático
Antes de mexer no Caddyfile, confira os pré-requisitos do certificado público: o registro A/AAAA do domínio apontando para o servidor e as portas 80 e 443 acessíveis pela internet. Sem isso a CA não consegue validar o domínio — e, se você errar muitas vezes, bate no limite de requisições dela.
Substitua o conteúdo de /etc/caddy/Caddyfile:
{
email admin@exemplo.com.br
}
www.exemplo.com.br {
root * /var/www/exemplo
file_server
encode zstd gzip
}
exemplo.com.br {
redir https://www.exemplo.com.br{uri} permanent
}
O bloco sem nome no topo guarda as opções globais; o e-mail identifica a conta ACME junto à CA, que pode usá-lo para avisos sobre a conta. Cada bloco seguinte é um site, identificado pelo endereço. Não há porta, caminho de certificado nem bloco de redirecionamento HTTP: ao ver um nome de domínio, o Caddy assume 443, obtém o certificado e responde na porta 80 com redirecionamento 308 para HTTPS.
sudo mkdir -p /var/www/exemplo
echo '<h1>Olá do Caddy</h1>' | sudo tee /var/www/exemplo/index.html
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
journalctl -u caddy --no-pager -n 30
O fmt padroniza a indentação (o Caddyfile usa tabulações), o validate carrega a configuração sem aplicá-la e o reload troca a configuração sem reiniciar o processo nem derrubar conexões HTTP comuns (WebSockets abertos são encerrados por padrão; a opção stream_close_delay do reverse_proxy adia esse fechamento). Não use restart nem stop para mudar configuração: a própria documentação avisa que parar o serviço causa indisponibilidade. No log aparece a obtenção do certificado; a partir daí, a renovação é automática.
Proxy reverso com balanceamento
O uso mais comum do Caddy é ficar na frente de aplicações que escutam só no loopback — um Gitea, um Vaultwarden, uma API em Node ou Go. Uma linha basta:
app.exemplo.com.br {
reverse_proxy 127.0.0.1:3000
}
O Caddy já repassa X-Forwarded-For, X-Forwarded-Proto e X-Forwarded-Host, e trata WebSocket sem configuração extra. Com mais de uma instância, liste os destinos e escolha a política:
(seguranca) {
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
-Server
}
}
app.exemplo.com.br {
reverse_proxy 127.0.0.1:8080 127.0.0.1:8081 {
lb_policy round_robin
health_uri /healthz
health_interval 10s
}
import seguranca
log {
output file /var/log/caddy/app.log
}
}
O bloco entre parênteses é um snippet: um trecho reutilizável que entra em qualquer site com import. O -Server remove o cabeçalho que identifica o servidor. A verificação ativa de saúde (health_uri) tira do rodízio a instância que parar de responder. O log sai em JSON, uma linha por requisição.
Testei exatamente essa configuração com dois backends: as requisições alternaram entre eles, os três cabeçalhos de segurança chegaram ao cliente, o cabeçalho Server sumiu e o HTTP respondeu 308 para HTTPS. Para validar localmente, sem domínio público, troque o nome por app.localhost: o Caddy emite o certificado pela CA interna (Caddy Local Authority), e o curl -k aceita.
Se você vem do nginx, compare com o bloco de proxy do Gogs: são dois server, quatro proxy_set_header e um certbot à parte para fazer o que esse arquivo faz.
PHP com php-fpm
Para WordPress, Nextcloud ou qualquer aplicação PHP, a diretiva php_fastcgi já traz as regras de reescrita para index.php. Ajuste o caminho do socket à versão instalada (8.4 no Debian 13, 8.3 no Ubuntu 24.04):
blog.exemplo.com.br {
root * /var/www/blog
php_fastcgi unix//run/php/php8.4-fpm.sock
file_server
encode zstd gzip
}
O usuário caddy já está no grupo www-data, o mesmo do php-fpm no Debian e no Ubuntu. Se você usa várias versões de PHP lado a lado, a lógica de pools e sockets do post Nginx e várias versões de PHP vale igual — muda só a linha do php_fastcgi.
Onde ficam certificados, logs e configuração
| Item | Caminho (pacote Debian/Ubuntu) |
|---|---|
| Configuração | /etc/caddy/Caddyfile |
| Certificados, chaves, conta ACME | /var/lib/caddy/.local/share/caddy |
CA local (sites .localhost) |
/var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt |
| Última configuração JSON salva | /var/lib/caddy/.config/caddy |
| Logs do serviço | journalctl -u caddy |
| Logs de acesso | onde a diretiva log mandar (no exemplo, /var/log/caddy/) |
O diretório de dados precisa ser persistente e entrar no backup: perder /var/lib/caddy significa reemitir todos os certificados, o que, com muitos domínios, pode esbarrar no limite da CA. Para variáveis secretas — um token de API de DNS, por exemplo —, use um drop-in do systemd em vez de escrever no Caddyfile:
sudo systemctl edit caddy
# no editor:
# [Service]
# EnvironmentFile=/etc/caddy/.env
Dentro do Caddyfile, a variável entra como {env.NOME}. A gestão de unidades e drop-ins está detalhada em Dominando o systemd.
A API de administração
Por padrão o Caddy abre uma API REST em localhost:2019, acessível apenas da própria máquina. É por ela que o systemctl reload caddy (que chama caddy reload) entrega a nova configuração. Você também pode consultá-la:
curl -s localhost:2019/config/ | jq .
caddy adapt --config /etc/caddy/Caddyfile --pretty | less
O primeiro comando mostra a configuração JSON em execução; o segundo, o JSON que o Caddyfile gera — útil para entender o que cada diretiva faz por baixo. Não exponha a porta 2019 na rede: quem chega nela troca a configuração do servidor. Se rodar código não confiável na mesma máquina, a documentação recomenda isolar processos ou trocar o endereço da API por um socket Unix com permissões restritas.
Docker
A imagem oficial é caddy no Docker Hub. Em 5 de outubro de 2026 ela ainda estava na 2.11.6 — dois dias atrás do pacote do apt —, por isso use a tag de série 2.11:
docker run -d --name caddy --restart unless-stopped \
-p 80:80 -p 443:443 -p 443:443/udp \
-v $PWD/Caddyfile:/etc/caddy/Caddyfile:ro \
-v caddy_data:/data \
-v caddy_config:/config \
caddy:2.11
O volume /data é o equivalente do /var/lib/caddy: sem ele, cada recriação do container pede certificados novos. A publicação 443:443/udp é a que habilita o HTTP/3. Para o básico de containers, veja o curso de Docker.
Plugins: xcaddy e add-package
O pacote oficial traz só os módulos padrão. Plugins — provedores de DNS para certificados wildcard, autenticação, rate limit — são compilados dentro do binário. O caminho recomendado é o xcaddy, que precisa do Go instalado:
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
xcaddy build --with github.com/caddy-dns/cloudflare
O resultado é um binário ./caddy no diretório atual. Para usá-lo com o serviço do pacote, siga a seção custom builds da documentação, que usa dpkg-divert para o apt não sobrescrevê-lo na próxima atualização. Existe também o caddy add-package, que baixa um binário com o plugin pronto do servidor de builds do projeto, mas ele ainda é marcado como experimental.
Caddy ou nginx?
O Caddy vence quando o problema é colocar serviços atrás de HTTPS com pouco atrito: VPS com meia dúzia de aplicações, homelab, ambientes de teste, ou equipes que não querem cuidar de certbot e renovação. O nginx continua forte onde já está instalado e afinado, em configurações muito específicas de cache e reescrita, e onde a equipe domina a sintaxe. Nada impede usar os dois: o Caddy na borda cuidando do TLS e o nginx atrás servindo uma aplicação legada.
Se você hospeda serviços como o Vaultwarden, o Gitea ou o Uptime Kuma, troque o bloco de nginx por três linhas de Caddyfile e compare.
Links
- caddyserver.com · documentação · github.com/caddyserver/caddy
- Automatic HTTPS · reverse_proxy · Running Caddy (systemd)
Um binário, um arquivo de configuração curto e nenhum certbot para lembrar de renovar: é essa a proposta do Caddy, e ela se sustenta. Instale pelo repositório oficial, mantenha /var/lib/caddy no backup, deixe a API em localhost e use reload, nunca restart, para aplicar mudanças.