⚡ AutomationsAI|Portal de Cursos →

Verificando acesso...

MÓDULO 6.2

🔒 Domínio, HTTPS, reverse proxy

Do A record ao cadeado verde: DNS para a VPS, comparativo de reverse proxies, Caddyfile com auto-HTTPS, subdomínios, headers de segurança e renovação automática do Let's Encrypt.

6
Tópicos
40
Minutos
Avançado
Nível
Prático
Tipo
1

🌐 DNS apontando para a VPS

HTTPS começa no DNS. Sem o domínio resolvendo para o IP da VPS, o Caddy não consegue completar o desafio ACME HTTP-01 do Let's Encrypt e o cadeado nunca aparece. O registro essencial é o A record (IPv4); se a VPS tem IPv6, adicione também um AAAA.

O TTL (time-to-live) controla quanto tempo resolvers fazem cache da resposta. Em mudança de IP, baixe para 300 antes; em produção estável, 3600 ou mais. A propagação global leva de minutos a algumas horas — depende do provider e do TTL anterior.

# Registros no painel do provider (Cloudflare, Registro.br, etc.)
# Tipo  Nome              Valor              TTL
# A     postiz.exemplo    203.0.113.42       300
# AAAA  postiz.exemplo    2001:db8::1        300
# A     api.postiz.exemplo 203.0.113.42       300
# A     app.postiz.exemplo 203.0.113.42       300

# Confirmar resolução (IPv4)
dig +short postiz.exemplo.com A
# 203.0.113.42

# IPv6
dig +short postiz.exemplo.com AAAA
# 2001:db8::1

# Resolver específico para ver propagação em outro DNS
dig @1.1.1.1 postiz.exemplo.com +short
dig @8.8.8.8 postiz.exemplo.com +short

💡 Dica prática

Antes de tentar emitir certificado, confirme que o domínio resolve para o IP certo a partir de fora — use dnschecker.org ou rode dig contra múltiplos resolvers. Let's Encrypt valida do datacenter deles, não do seu laptop.

Conceitos-chave

A record

Mapeia hostname → IPv4 da VPS.

AAAA

Versão IPv6 do A record.

TTL

Segundos que resolvers guardam o resultado em cache.

Propagação

Tempo até a mudança aparecer em todos os resolvers.

2

🔄 Caddy vs Traefik vs nginx-proxy

Três caminhos para o mesmo objetivo: terminar TLS na borda e fazer reverse proxy para os containers. A escolha depende de quanto automatismo você quer e quão familiar é com cada ecossistema.

Caddy

Auto-HTTPS por padrão. Caddyfile lê como receita. Zero configuração para o caso comum.

Use quando: quer o menor caminho até o cadeado, stack pequeno ou médio.

Traefik

Configuração por labels do Docker — containers se anunciam sozinhos. Dashboard nativo.

Use quando: muitos serviços dinâmicos, Swarm ou Kubernetes na frente.

nginx-proxy

nginx + acme-companion. Máximo controle, sintaxe nginx tradicional, ecossistema enorme.

Use quando: já domina nginx, precisa de tuning fino ou módulos específicos.

Para o Postiz em uma VPS, Caddy é a escolha óbvia: emite e renova certificado sozinho, suporta HTTP/2 e HTTP/3 sem flags, e o arquivo de config cabe em 10 linhas.

# Comparativo rápido (linhas de config para o caso "1 domínio → 1 backend")
# Caddy        ~3 linhas
# Traefik      ~25 linhas (labels) + traefik.yml estático
# nginx-proxy  ~20 linhas + Dockerfile de acme-companion

# Footprint de memória (idle, médio)
# Caddy        ~30 MB
# Traefik      ~50 MB
# nginx        ~10 MB (sem acme-companion)

Conceitos-chave

Reverse proxy

Recebe na porta 443 e encaminha para o backend interno.

Auto-HTTPS

Emite, instala e renova certificado sem intervenção.

Service discovery

Traefik descobre containers por labels.

TLS termination

HTTPS termina na borda; backend fala HTTP no interno.

3

📝 Caddyfile mínimo + auto-HTTPS

O Caddy adiciona à stack como mais um serviço no docker-compose.yml. Ele assume as portas 80 e 443 do host, e fala com o Postiz pela network interna — sem expor 5000 publicamente.

# ~/postiz/Caddyfile
postiz.exemplo.com {
    reverse_proxy postiz:5000
    encode gzip zstd
}

Três linhas. O Caddy faz o resto: ACME challenge, certificado, redirect 80→443, HTTP/2, HTTP/3. Adicione o serviço ao compose e remova o ports: ["5000:5000"] do Postiz — ele não precisa mais ser acessível direto.

# ~/postiz/docker-compose.yml (recorte do serviço caddy)
  caddy:
    image: caddy:2-alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"   # HTTP/3 (QUIC)
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data         # certificados (NÃO PERDER!)
      - caddy-config:/config
    networks: [postiz-net]
    depends_on:
      - postiz

volumes:
  caddy-data:
  caddy-config:

Dica prática

O volume caddy-data guarda os certificados emitidos. Apagá-lo força o Caddy a pedir certificado novo — e o Let's Encrypt tem rate limit de 5 emissões por hostname por semana. Trate esse volume como banco de dados.

Conceitos-chave

Caddyfile

Sintaxe declarativa, um site por bloco.

reverse_proxy

Encaminha requests para um backend interno.

ACME

Protocolo do Let's Encrypt para emissão automática.

HTTP/3

Transporte sobre QUIC/UDP — porta 443 UDP.

4

🌍 Subdomínios (api., app.)

Frontend e backend em subdomínios separados dá clareza operacional: app.exemplo.com serve a UI, api.exemplo.com a API. Cada subdomínio é um bloco no Caddyfile com seu próprio certificado.

# ~/postiz/Caddyfile com subdomínios
app.exemplo.com {
    reverse_proxy postiz:5000
    encode gzip zstd
}

api.exemplo.com {
    reverse_proxy postiz:3000
    encode gzip zstd

    # CORS para o frontend
    header {
        Access-Control-Allow-Origin "https://app.exemplo.com"
        Access-Control-Allow-Credentials "true"
    }
}

# Redirect raiz → app
exemplo.com {
    redir https://app.exemplo.com{uri} permanent
}

Ajuste as variáveis do Postiz para refletir os subdomínios — FRONTEND_URL=https://app.exemplo.com, NEXT_PUBLIC_BACKEND_URL=https://api.exemplo.com. Sem isso o frontend tenta chamar localhost e quebra no browser do usuário.

✓ O que FAZER

  • Criar DNS A para CADA subdomínio antes do caddy reload.
  • Atualizar FRONTEND_URL e NEXT_PUBLIC_BACKEND_URL no .env.
  • Restringir Access-Control-Allow-Origin ao domínio do frontend.
  • Usar redirect 308 do apex para o subdomínio canônico.

✗ O que NÃO fazer

  • Liberar Access-Control-Allow-Origin: * com credenciais.
  • Misturar HTTP e HTTPS — sempre force HTTPS no canônico.
  • Esquecer wildcard quando há mais de 3-4 subdomínios.
  • Hardcodar URLs no frontend — sempre via env var.

Conceitos-chave

Subdomínio

Hostname filho do domínio principal.

CORS

Browser bloqueia origens cruzadas sem header explícito.

Redirect 308

Permanente, preserva método HTTP original.

Apex

Domínio sem subdomínio (exemplo.com).

5

🛡️ Headers de segurança

Certificado é só metade do trabalho. Sem headers de segurança, o browser ainda permite ataques que TLS sozinho não cobre: downgrade para HTTP, clickjacking, sniffing de MIME, XSS via scripts injetados.

🧱 Os 4 headers que toda app web deve ter

  • Strict-Transport-Security (HSTS): obriga o browser a usar HTTPS pelos próximos N segundos, mesmo se o usuário digitar http://.
  • X-Frame-Options: impede que seu site seja carregado dentro de um <iframe> em outro domínio (clickjacking).
  • X-Content-Type-Options: nosniff impede o browser de adivinhar o tipo MIME e executar scripts disfarçados.
  • Content-Security-Policy (CSP): whitelist do que pode ser carregado (scripts, styles, imagens, etc.) — defesa em profundidade contra XSS.
# ~/postiz/Caddyfile com headers de segurança
app.exemplo.com {
    reverse_proxy postiz:5000
    encode gzip zstd

    header {
        # HTTPS obrigatório por 1 ano, incluindo subdomínios
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

        # Anti-clickjacking
        X-Frame-Options "DENY"

        # Anti-MIME-sniffing
        X-Content-Type-Options "nosniff"

        # Referrer só para mesma origem
        Referrer-Policy "strict-origin-when-cross-origin"

        # CSP básico — ajuste conforme assets externos do app
        Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self'"

        # Remove header que vaza versão do servidor
        -Server
    }
}

⚠️ Dica prática

O preload do HSTS é irreversível por meses — só ative depois de confirmar que TUDO funciona em HTTPS, inclusive subdomínios futuros. Comece com max-age=300 em teste e suba aos poucos.

Conceitos-chave

HSTS

Force HTTPS no browser por tempo determinado.

Clickjacking

Site embutido em iframe inimigo para roubar cliques.

CSP

Whitelist de origens permitidas para cada tipo de asset.

MIME sniffing

Browser adivinhando tipo do conteúdo — bloqueado por nosniff.

6

🔁 Renovação automática Let's Encrypt

Certificado do Let's Encrypt dura 90 dias. O Caddy renova automaticamente quando faltam ~30 dias — sem cron, sem script, sem intervenção. O que pode dar errado é o desafio ACME falhar em silêncio; o jeito de saber é olhar os logs.

# Acompanhar logs do Caddy (boot + renovações)
docker compose logs -f caddy

# Procurar especificamente eventos de certificado
docker compose logs caddy | grep -iE "certificate|acme|renew"

# Inspecionar certificado emitido (data de expiração)
echo | openssl s_client -servername app.exemplo.com -connect app.exemplo.com:443 2>/dev/null \
  | openssl x509 -noout -dates -issuer

# Forçar reload do Caddyfile sem reiniciar (zero downtime)
docker exec caddy caddy reload --config /etc/caddy/Caddyfile

# Caso renovação falhe: limpar e tentar de novo (cuidado com rate limit!)
docker compose restart caddy
1

Volume caddy-data persistente

Sem ele, cada restart pede certificado novo e bate no rate limit do Let's Encrypt.

2

Portas 80 e 443 abertas no firewall

ACME HTTP-01 precisa de 80; TLS precisa de 443. Bloquear qualquer uma quebra renovação.

3

DNS continuando a apontar para o IP correto

Trocou de VPS? Atualize o A record antes de tentar renovar.

4

Monitorar logs ao menos 1x/mês

Procure por obtained certificate ou renewed certificate nos últimos 30 dias.

🚨 Dica prática

Configure um alerta externo (UptimeRobot, healthchecks.io) que bate em https://app.exemplo.com a cada 5 min. Se o cert vencer ou a config quebrar, você descobre antes dos usuários — não pelo print no WhatsApp dizendo "site fora do ar".

Conceitos-chave

Let's Encrypt

CA gratuita, certs de 90 dias via protocolo ACME.

HTTP-01

Desafio servindo arquivo em /.well-known/acme-challenge/.

Rate limit

5 emissões/semana/hostname no Let's Encrypt prod.

Zero downtime

caddy reload troca config sem dropar conexões.

🎯 Resumo do Módulo

DNS apontando para a VPS — A record (e AAAA, se IPv6), TTL baixo durante mudança, propagação confirmada via dig.
Reverse proxy escolhido — Caddy para o caso comum, Traefik para stacks dinâmicos, nginx-proxy quando precisa de tuning fino.
Caddyfile mínimo no ar — 3 linhas + serviço no compose, volume caddy-data persistente.
Subdomínios separadosapp. e api. em blocos próprios, CORS restrito ao frontend.
Headers de segurança — HSTS, X-Frame-Options, X-Content-Type-Options e CSP básico ativos.
Renovação automática — Caddy renova a cada ~60 dias, logs monitorados e alerta externo configurado.

Próximo Módulo:

6.3 — Backup, monitoramento e logs