Caddy Reverse Proxy

Configuración del reverse proxy

Estructura de Archivos

/etc/caddy/
├── Caddyfile           # Configuración principal
├── sites.d/            # Vhosts por proyecto
│   ├── proyecto1.caddy
│   ├── proyecto2.caddy
│   └── ...
└── includes/           # Snippets reutilizables
    └── tls_cf.caddy

Caddyfile Principal

{
    acme_dns cloudflare {
        api_token {env.CLOUDFLARE_API_TOKEN}
    }
}

# Snippet para TLS via Cloudflare
(tls_cf) {
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }
}

# Importar todos los vhosts
import sites.d/*.caddy

Configuración de Proyecto

Cada proyecto tiene su .caddy en deploy/:

# /srv/projects/mi-proyecto/deploy/caddy.caddy
mi-proyecto.illanes00.cl {
    import tls_cf
    encode zstd gzip

    # Health check separado
    @health path /health /healthz /readyz
    handle @health {
        reverse_proxy localhost:8105
    }

    # Todo lo demás
    reverse_proxy localhost:8105
}

Comandos Frecuentes

# Recargar configuración (sin downtime)
sudo systemctl reload caddy

# Validar sintaxis
caddy validate --config /etc/caddy/Caddyfile

# Formatear archivos
caddy fmt --overwrite /etc/caddy/Caddyfile

# Ver logs
journalctl -u caddy -f -n 50

# Ver certificados activos
sudo ls -la /var/lib/caddy/.local/share/caddy/certificates/

Patrones Comunes

Redirect WWW → Apex

www.ejemplo.com {
    redir https://ejemplo.com{uri} permanent
}

SPA con fallback

app.ejemplo.com {
    import tls_cf
    root * /srv/static/app
    try_files {path} /index.html
    file_server
}

API con rate limiting

api.ejemplo.com {
    import tls_cf
    rate_limit {
        zone api {
            match {
                path /api/*
            }
            key {remote_host}
            rate 100/m
        }
    }
    reverse_proxy localhost:8103
}

Troubleshooting

“ambiguous site definition”

Un dominio está definido en más de un archivo .caddy.

# Buscar duplicados
grep -r "dominio.ejemplo.com" /etc/caddy/

Certificado no se genera

# Ver logs de ACME
journalctl -u caddy | grep -i acme

# Verificar token de Cloudflare
curl -X GET "https://api.cloudflare.com/client/v4/zones" \
  -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"

502 Bad Gateway

El upstream no está respondiendo.

# Verificar que el servicio está corriendo
systemctl status mi-proyecto.service

# Verificar puerto
ss -tlnp | grep 8105