Como configurar HTTPS local com Caddy em projetos de desenvolvimento

1. Por que usar HTTPS local no desenvolvimento?

Desenvolver com HTTPS local não é apenas um luxo — é uma necessidade técnica. Ambientes de produção modernos exigem conexões seguras, e replicar isso localmente evita surpresas desagradáveis. A diferença entre HTTP e HTTPS no ambiente local vai além do cadeado verde no navegador: cookies com flag Secure, Service Workers, geolocalização e notificações push simplesmente não funcionam em HTTP.

Além disso, simular produção com HTTPS previne problemas de mixed content — quando uma página carregada por HTTPS tenta carregar recursos HTTP — e evita dores de cabeça com CORS em APIs que exigem origens seguras. Sem HTTPS local, você descobre esses problemas apenas no deploy, o que custa tempo e retrabalho.

2. Conhecendo o Caddy como servidor de desenvolvimento

Caddy é um servidor web moderno escrito em Go que se destaca por uma característica única: HTTPS automático. Diferente de nginx ou Apache, que exigem configuração manual de certificados TLS, o Caddy obtém, renova e gerencia certificados automaticamente via ACME (Let's Encrypt). Para desenvolvimento local, ele oferece certificados autoassinados confiáveis sem precisar de ferramentas auxiliares como mkcert.

Vantagens do Caddy:
- Configuração declarativa e legível via Caddyfile
- HTTPS automático sem scripts externos
- Proxy reverso nativo com balanceamento de carga
- Suporte a HTTP/2 e HTTP/3
- Baixo consumo de recursos

Instalação:

# Linux (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

# macOS
brew install caddy

# Windows (via chocolatey)
choco install caddy

3. Configuração básica do Caddyfile para HTTPS local

O Caddyfile é o coração da configuração. Sua estrutura é simples: domínio seguido de diretivas entre chaves.

Exemplo mínimo — proxy reverso para app Node.js na porta 3000:

app.localhost {
    reverse_proxy localhost:3000
}

Salve como Caddyfile e execute:

caddy run

O Caddy automaticamente criará um certificado autoassinado para app.localhost. Acesse https://app.localhost no navegador — você verá um aviso de certificado não confiável, que pode ser ignorado em desenvolvimento.

Para domínios reais (ex.: meuapp.test), o Caddy tentará obter certificados Let's Encrypt. Para desenvolvimento offline, use tls internal.

4. Gerenciamento de certificados TLS com Caddy

O Caddy gerencia certificados de forma transparente. Quando você define um domínio público (ex.: exemplo.com), ele automaticamente solicita certificados Let's Encrypt via ACME. Para ambientes offline ou testes, use a diretiva tls internal:

app.localhost {
    tls internal
    reverse_proxy localhost:3000
}

Isso gera certificados autoassinados que o Caddy armazena em seu diretório de configuração (~/.local/share/caddy ou equivalente no Windows). Para confiar nesses certificados no navegador, você pode importar a CA raiz do Caddy:

# Linux
sudo cp ~/.local/share/caddy/pki/authorities/local/root.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

# macOS
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ~/Library/Application\ Support/Caddy/pki/authorities/local/root.crt

5. Configurações avançadas para projetos reais

Proxy reverso para múltiplos serviços:

api.localhost {
    tls internal
    reverse_proxy localhost:4000
}

app.localhost {
    tls internal
    reverse_proxy localhost:3000
}

static.localhost {
    tls internal
    root * /var/www/meuapp/static
    file_server
}

Headers de segurança essenciais:

app.localhost {
    tls internal
    reverse_proxy localhost:3000

    header / {
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        Content-Security-Policy "default-src 'self'"
    }
}

Logs e debugging:

app.localhost {
    tls internal
    reverse_proxy localhost:3000

    log {
        output file /var/log/caddy/access.log
        format json
    }

    errors {
        log file /var/log/caddy/errors.log
    }
}

6. Integração com Docker e ambientes conteinerizados

Rodar Caddy em Docker é ideal para ambientes de desenvolvimento replicáveis.

Dockerfile para Caddy:

FROM caddy:alpine

COPY Caddyfile /etc/caddy/Caddyfile
COPY site /usr/share/caddy

docker-compose.yml completo:

version: '3.8'

services:
  caddy:
    image: caddy:alpine
    container_name: caddy-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - dev-network

  node-app:
    build: ./node-app
    container_name: node-backend
    expose:
      - "3000"
    networks:
      - dev-network

  react-app:
    build: ./react-app
    container_name: react-frontend
    expose:
      - "5173"
    networks:
      - dev-network

volumes:
  caddy_data:
  caddy_config:

networks:
  dev-network:
    driver: bridge

Caddyfile para Docker:

app.localhost {
    tls internal
    reverse_proxy node-app:3000
}

admin.localhost {
    tls internal
    reverse_proxy react-app:5173
}

7. Resolução de problemas comuns

Certificados não confiáveis no navegador:
- Verifique se a CA raiz do Caddy foi importada corretamente
- Em navegadores Chrome/Edge, digite thisisunsafe na tela de aviso para acesso temporário
- Firefox permite adicionar exceção permanente

Conflitos de porta:
- Verifique se nenhum outro serviço está usando as portas 80/443
- Use sudo lsof -i :80 (Linux/macOS) para identificar processos
- Configure portas alternativas: app.localhost:8080 { ... }

Erros de proxy reverso:
- Confirme se o serviço de destino está rodando e acessível
- Verifique nomes de containers no Docker: use o nome do serviço, não localhost
- Aumente timeouts: reverse_proxy localhost:3000 { transport http { read_timeout 30s } }

8. Boas práticas e próximos passos

Automatize a inicialização:
- Adicione um script start-dev.sh que executa caddy run junto com outros serviços
- Use caddy start para rodar em background e caddy stop para encerrar

Versionamento do Caddyfile:
- Mantenha o Caddyfile no repositório do projeto
- Use variáveis de ambiente para configurações sensíveis: {$DOMAIN} no Caddyfile

Comparação com alternativas:
- Traefik: mais complexo, mas excelente para orquestração com Docker Swarm/Kubernetes
- ngrok: ideal para expor servidor local temporariamente, mas não substitui HTTPS local persistente
- mkcert: útil para certificados locais, mas exige configuração manual em cada projeto

Caddy oferece o melhor equilíbrio entre simplicidade e potência para desenvolvimento local com HTTPS. Comece com configurações básicas e evolua conforme seu projeto cresce.

Referências