TL;DR — Resumen Rápido

Guía completa de Caddy como proxy inverso con HTTPS automático. Caddyfile, balanceo de carga, health checks y Caddyfile de producción para múltiples sitios.

Caddy es un servidor web moderno de código abierto escrito en Go cuya característica definitoria es el HTTPS automático: provisiona y renueva certificados TLS desde Let’s Encrypt o ZeroSSL en el momento en que apuntas un dominio hacia él, sin ninguna configuración manual de Certbot. Como proxy inverso, Caddy combina esa gestión de TLS sin configuración con una sintaxis declarativa limpia, balanceo de carga integrado, HTTP/2 y HTTP/3 habilitados por defecto, y un único binario compilado estáticamente sin dependencias externas. Esta guía cubre todo lo necesario para ejecutar Caddy como proxy inverso en producción: instalación, sintaxis del Caddyfile, todas las políticas de balanceo de carga, health checks activos y pasivos, manipulación de cabeceras, autenticación, TLS bajo demanda, la API de administración y un Caddyfile completo para múltiples sitios.

Por Qué Elegir Caddy como Proxy Inverso

Antes de entrar en la configuración, aquí hay una comparación directa de las principales opciones de proxy inverso disponibles en 2026:

CaracterísticaCaddyNginxTraefikHAProxyApache
HTTPS automáticoCliente ACME integradoManual (Certbot)Integrado (vía ACME)ManualManual (mod_md)
Formato de configuraciónCaddyfile (mínimo)nginx.conf (verboso)YAML/TOML/etiquetas Dockerhaproxy.cfghttpd.conf
HTTP/2Por defectoConfiguración explícitaPor defectoNo (solo TCP)Configuración explícita
HTTP/3 (QUIC)Por defectoExperimentalVía pluginNoNo
Binario únicoSí (Go, sin deps)No (C, con módulos)Sí (Go)Sí (C)No
API de configuraciónAPI REST completaNoAPI REST completaSolo socket de estadísticasNo
Balanceo de carga8 políticas integradasLimitado integradoMúltiples proveedoresExcelenteBásico
Huella de memoria~20-50 MB~5-15 MB~25-60 MB~5-10 MB~30-80 MB
Curva de aprendizajeBajaMedia-AltaMediaAltaMedia-Alta

Elige Caddy cuando: quieras TLS automático, configuración mínima y HTTP/3 sin builds de plugins ni herramientas externas. Ideal para aplicaciones auto-alojadas, proxy inverso multi-tenant y equipos que quieren cero sobrecarga de gestión de certificados.

Prerrequisitos

  • Un servidor Linux con Ubuntu 22.04+, Debian 12+ o una distribución compatible con RHEL/CentOS
  • Un nombre de dominio con registros DNS A/AAAA apuntando a la IP pública del servidor
  • Los puertos 80 y 443 abiertos en el firewall (requeridos para el desafío HTTP-01 de ACME y el tráfico HTTPS)
  • Acceso root o sudo
  • Ningún otro proceso vinculado a los puertos 80 o 443
# Abrir puertos requeridos con UFW
sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw status

Instalación de Caddy

Ubuntu y Debian (APT)

sudo apt update && 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 apt update && sudo apt install -y caddy
caddy version

RHEL, CentOS, Fedora (DNF)

dnf install 'dnf-command(copr)'
dnf copr enable @caddy/caddy
dnf install caddy

Docker

docker run -d \
  -p 80:80 -p 443:443 -p 443:443/udp \
  -v /ruta/a/Caddyfile:/etc/caddy/Caddyfile \
  -v caddy_data:/data \
  -v caddy_config:/config \
  caddy:latest

xcaddy — Compilaciones Personalizadas con Plugins

go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

xcaddy build \
  --with github.com/caddy-dns/cloudflare \
  --with github.com/mholt/caddy-ratelimit

sudo mv caddy /usr/bin/caddy && sudo systemctl restart caddy

Sintaxis del Caddyfile

Estructura

# Bloque de opciones globales
{
    email admin@ejemplo.com
    admin off
    grace_period 10s
}

# Bloque de sitio — el dominio activa HTTPS automático
app.ejemplo.com {
    directiva argumento
}

Matchers

ejemplo.com {
    @api path /api/*
    reverse_proxy @api localhost:8000

    reverse_proxy /static/* localhost:9000

    file_server *
}

Marcadores de posición (Placeholders)

ejemplo.com {
    reverse_proxy localhost:3000 {
        header_up X-IP-Real        {remote_host}
        header_up X-Puerto-Reenv   {server_port}
        header_up X-ID-Solicitud   {uuid}
    }
}

Configuración del Proxy Inverso

Backend Único Básico

app.ejemplo.com {
    reverse_proxy localhost:3000
}

Esta única línea proporciona: HTTPS automático, HTTP/2, HTTP/3, redirección HTTP→HTTPS, reenvío de cabeceras X-Forwarded-For y soporte transparente de WebSocket.

Enrutamiento Basado en Rutas

ejemplo.com {
    reverse_proxy /api/*    localhost:8000
    reverse_proxy /ws/*     localhost:4000
    reverse_proxy /admin/*  localhost:9000

    root * /var/www/frontend
    file_server
}

Manipulación de Cabeceras

app.ejemplo.com {
    reverse_proxy localhost:3000 {
        header_up Host              {upstream_hostport}
        header_up X-IP-Real         {remote_host}
        header_up X-Proto-Reenviado {scheme}

        header_up -Authorization

        header_down -X-ID-Servidor-Interno
        header_down Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
    }
}

WebSocket y Conexiones de Larga Duración

ws.ejemplo.com {
    reverse_proxy localhost:4000 {
        flush_interval -1
    }
}

Balanceo de Carga

Múltiples Backends

app.ejemplo.com {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003
}

Todas las Políticas de Balanceo de Carga

app.ejemplo.com {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy least_conn         # Backend con menos conexiones activas
        # lb_policy round_robin      # Rotación secuencial (por defecto)
        # lb_policy first            # Siempre el primer backend disponible
        # lb_policy random           # Selección aleatoria
        # lb_policy ip_hash          # IP del cliente → backend consistente
        # lb_policy cookie caddy_lb  # Persistencia de sesión basada en cookie
        # lb_policy header X-Shard   # Enrutamiento por valor de cabecera
        # lb_policy uri_hash         # URI de solicitud → backend consistente
    }
}

Health Checks Activos

app.ejemplo.com {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy least_conn

        health_uri      /health
        health_interval 15s
        health_timeout  5s
        health_status   200
        health_headers  Authorization "Bearer token-healthcheck"
    }
}

Health Checks Pasivos

app.ejemplo.com {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy round_robin

        fail_duration     30s
        max_fails         3
        unhealthy_latency 500ms
        unhealthy_status  5xx
    }
}

HTTPS Automático en Profundidad

Tipos de Desafío ACME

{
    email admin@ejemplo.com
}

# HTTP-01 (por defecto): Caddy sirve el desafío en el puerto 80
ejemplo.com {
    reverse_proxy localhost:3000
}

# TLS-ALPN-01: Usa el puerto 443, funciona cuando el puerto 80 está bloqueado
solo-tls.ejemplo.com {
    tls {
        challenges tls-alpn-01
    }
    reverse_proxy localhost:3001
}

# DNS-01: Sin puertos entrantes — requiere xcaddy + plugin DNS
wildcard.ejemplo.com {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
    reverse_proxy localhost:3002
}

TLS Bajo Demanda para Dominios Dinámicos

{
    on_demand_tls {
        ask      http://localhost:9001/verificar-dominio
        interval 2m
        burst    5
    }
}

:443 {
    tls {
        on_demand
    }
    reverse_proxy localhost:3000
}

CA Interna para Desarrollo

{
    local_certs
}

localhost, 127.0.0.1, ::1 {
    reverse_proxy localhost:3000
}

Autenticación

Autenticación Básica HTTP

admin.ejemplo.com {
    basicauth /admin/* {
        alice $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GT.VVKf3KCrXxpHPkAXmn9sQHO
    }
    reverse_proxy localhost:9000
}
# Generar hash de contraseña
caddy hash-password --plaintext 'tucontraseña'

Autenticación Delegada (Authelia, oauth2-proxy)

app.ejemplo.com {
    forward_auth authelia:9091 {
        uri /api/verify?rd=https://auth.ejemplo.com
        copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
    }
    reverse_proxy localhost:3000
}

Servidor de Archivos y SPA

Servidor de Archivos Estáticos

estatico.ejemplo.com {
    root * /var/www/html
    encode gzip zstd
    file_server {
        hide .git .env .htaccess
    }
}

Aplicación de Página Única (SPA)

spa.ejemplo.com {
    root * /var/www/app/dist
    encode gzip zstd
    try_files {path} /index.html
    file_server
}

Compresión y Registro de Acceso

ejemplo.com {
    encode {
        zstd
        gzip 6
        minimum_length 1024
    }

    log {
        output file /var/log/caddy/acceso.log {
            roll_size    100MiB
            roll_keep    10
            roll_keep_for 720h
        }
        format json
        level  INFO
    }

    reverse_proxy localhost:3000
}

La API de Administración de Caddy

# Ver la configuración actual
curl http://localhost:2019/config/

# Recargar configuración desde Caddyfile
curl -X POST http://localhost:2019/load \
  -H "Content-Type: text/caddyfile" \
  --data-binary @/etc/caddy/Caddyfile

Caddyfile de Producción para Múltiples Sitios

{
    email admin@ejemplo.com
    grace_period 30s
    admin unix//run/caddy/admin.sock
}

(cabeceras_seguridad) {
    header {
        Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
        X-Content-Type-Options    "nosniff"
        X-Frame-Options           "SAMEORIGIN"
        Referrer-Policy           "strict-origin-when-cross-origin"
        -Server
        -X-Powered-By
    }
}

app.ejemplo.com {
    import cabeceras_seguridad
    encode zstd gzip

    log {
        output file /var/log/caddy/app-acceso.log { roll_size 50MiB; roll_keep 7 }
        format json
    }

    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy       least_conn
        health_uri      /health
        health_interval 10s
        health_timeout  3s
        fail_duration   30s
        max_fails       3

        header_up X-IP-Real         {remote_host}
        header_up X-Proto-Reenviado {scheme}
        header_down -X-Traza-Interna
    }
}

www.ejemplo.com {
    import cabeceras_seguridad
    root * /var/www/frontend/dist
    encode zstd gzip

    @estatico path *.css *.js *.png *.jpg *.webp *.gif *.svg *.woff2 *.ico
    header @estatico Cache-Control "public, max-age=31536000, immutable"

    try_files {path} /index.html
    file_server
}

Errores Comunes y Soluciones

El puerto 80 debe ser accesible para los desafíos HTTP-01. Si tu proveedor de nube o firewall bloquea el puerto 80, usa el desafío tls-alpn-01 o un desafío DNS-01 con xcaddy y un plugin DNS.

Límites de tasa de Let’s Encrypt. El servidor ACME de producción permite 5 certificados duplicados por dominio por semana. Durante las pruebas, establece acme_ca https://acme-staging-v02.api.letsencrypt.org/directory.

El usuario caddy necesita acceso de lectura a tus raíces web. El servicio systemd se ejecuta como el usuario del sistema caddy. Siempre ejecuta sudo chown -R caddy:caddy /var/www/tusite.

El intervalo de vaciado importa para el streaming. Los eventos enviados por el servidor (SSE) y las APIs de streaming requieren flush_interval -1 dentro del bloque reverse_proxy.

Solución de Problemas

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

# Formatear el Caddyfile automáticamente
caddy fmt --overwrite /etc/caddy/Caddyfile

# Ver los registros en tiempo real
sudo journalctl -u caddy -f --no-pager

# Verificar en qué puertos escucha Caddy
sudo ss -tlnp | grep caddy

# Verificar el certificado TLS
echo | openssl s_client -connect ejemplo.com:443 -servername ejemplo.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

Resumen

  • Caddy es un único binario Go sin dependencias externas que proporciona HTTPS automático vía ACME, HTTP/2 y HTTP/3 por defecto, y una sintaxis Caddyfile mínima
  • La directiva reverse_proxy maneja el proxying, balanceo de carga, manipulación de cabeceras, soporte WebSocket y health checks en un único bloque
  • Ocho políticas de balanceo de carga cubren todos los escenarios, desde round-robin sin estado hasta afinidad de sesión basada en cookies
  • Los health checks activos y pasivos pueden combinarse para una detección robusta de fallos
  • El TLS bajo demanda permite la emisión de certificados por tenant para plataformas SaaS sin preconfiguración
  • Usa snippets para compartir bloques de configuración entre múltiples definiciones de sitios

Artículos Relacionados