Servidores

Cómo configurar Caddy como reverse proxy en Linux 2026: auto-HTTPS incluido

Caddy es el único servidor web mainstream que obtiene y renueva certificados TLS solo, sin que toques nada. Si quieres HTTPS sin pelearte con certbot cada 90 días, esta guía es para ti.

TL;DR — Lo que vas a tener al final

  • Tiempo de setup: 30 minutos a 2 horas dependiendo de la complejidad del entorno.
  • Requisitos: Acceso root, Linux moderno, conexión a internet para descargar dependencias.
  • Resultado: Servicio funcional y estable, documentado paso a paso.
  • Troubleshooting: Logs estándar en /var/log/, comandos de verificación al final de cada sección.

Por qué Caddy y no Nginx o Traefik

Caddy 2 existe desde 2020 y su propuesta es simple: HTTPS por defecto. Mientras que Nginx requiere que tú corras certbot y le pegues los certificados al server { listen 443 ssl; }, Caddy los solicita solo la primera vez que alguien llega al puerto 443 y los renueva solo cuando faltan 30 días para vencer.

En homelabs esto significa:

  • Cero cron jobs para renovar certificados
  • Cero scripts de acme.sh colgados después de un reinicio
  • Misma config local y producción: el Caddyfile que probás en casa funciona igual arriba
  • HTTP/3 habilitado por defecto: los visitantes negocian QUIC si su navegador lo soporta, sin tocar nada

La contra conocida: el ecosistema de módulos es más chico que el de Nginx. Si necesitas reglas muy específicas (rate limit a nivel de location, geoip, etc.), vas a extrañar Nginx. Pero para el 90% de los casos de homelab (varios servicios detrás de un único punto de entrada), Caddy es la opción más corta.

Requisitos

  • Un servidor Linux con Debian 12+, Ubuntu 22.04+ o RHEL 9+
  • Puertos 80 y 443 libres en el firewall (Caddy los necesita para ACME)
  • DNS del dominio apuntando a la IP pública del servidor (registro A o AAAA)
  • Acceso root o sudo

Paso 1: Instalar Caddy

El paquete oficial está en el repo apt.caddy.community. Agregalo antes de instalar, porque la versión de los repos de Debian/Ubuntu suele estar uno o dos releases atrás.

sudo apt install -y debian-keyring debian-archive-keyring
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

debian-keyring y debian-archive-keyring son las dependencias para que apt verifique la firma del repo nuevo. Si te salta el error NO_PUBKEY al hacer apt update, es que la importación con --dearmor no terminó bien: repetila y verifica que el archivo en /usr/share/keyrings/ no esté vacío.

Después de instalar, Caddy queda corriendo como servicio systemd escuchando en :80 y :443. verifica:

caddy version
# v2.x.x
sudo systemctl status caddy
# active (running)

Si systemctl status muestra inactive o failed, corré sudo journalctl -u caddy -n 50 para ver el motivo. El motivo más común en un server fresco es que el puerto 80 ya está ocupado por Nginx o Apache: paralo (sudo systemctl stop nginx) y reiniciá Caddy (sudo systemctl restart caddy).

Paso 2: Tu primer reverse proxy

El archivo de configuración principal es el Caddyfile, vive en /etc/caddy/Caddyfile. Por defecto viene con un localhost:2019 que es la API interna — sobreescribilo con tu config.

sudo nano /etc/caddy/Caddyfile

Pegá esta config mínima:

midominio.com {
    reverse_proxy 127.0.0.1:8080
}

reverse_proxy 127.0.0.1:8080 le dice a Caddy: cuando alguien llegue a midominio.com, pasá la request al servicio local que escucha en el puerto 8080. El 8080 es el típico de una app web dev (Grafana, Jellyfin, un app de Node, etc.).

Validá la sintaxis antes de aplicar:

caddy validate --config /etc/caddy/Caddyfile

Si la validación pasa, recargá Caddy sin tirar conexiones activas:

sudo systemctl reload caddy

reload (no restart) hace que Caddy recargue la config manteniendo los workers vivos. Es el equivalente a nginx -s reload. Ahora abrí https://midominio.com en el navegador y deberías ver tu app detrás de HTTPS válido, con candado verde y todo — sin tocar un solo certificado.

Paso 3: Múltiples dominios en un solo servidor

El caso clásico del homelab: varios servicios, un único puerto público (443), cada uno en su subdominio.

grafana.midominio.com {
    reverse_proxy localhost:3000
}
jellyfin.midominio.com {
    reverse_proxy localhost:8096
}
docs.midominio.com {
    reverse_proxy localhost:8000
}

Caddy enruta por Host automáticamente. No necesitas definir server { server_name ... } como en Nginx — el primer match gana y listo. Cada bloque obtiene su propio certificado de Let’s Encrypt sin que pongas nada: Caddy emite un cert por cada host distinto que ve en el config.

Para aplicar cambios sin tirar el server:

sudo systemctl reload caddy

Si un subdominio no levanta HTTPS, dos causas frecuentes: (1) el DNS no apunta al server todavía (dig +short grafana.midominio.com debe devolver tu IP), o (2) el puerto 80 está bloqueado por algún firewall/cloud — Let’s Encrypt solo emite si puede llegar al puerto 80 con HTTP-01 challenge.

Paso 4: Configuraciones avanzadas que vas a necesitar

Path-based routing

Si tienes un solo dominio y quieres servir varios servicios por path:

midominio.com {
    reverse_proxy /api/* localhost:3000
    reverse_proxy localhost:8080
}

/api/* matchea todo lo que empiece con /api/ y lo manda al backend 3000; el resto va al 8080. Útil cuando no quieres administrar DNS para subdominios separados.

Cache estático

Para que Caddy sirva él mismo los archivos estáticos sin pegarle al backend:

midominio.com {
    reverse_proxy localhost:8080
    @assets path /assets/*
    handle @assets {
        root * /var/www/miapp/public
        file_server
        header Cache-Control "public, max-age=31536000, immutable"
    }
}

file_server sirve los archivos del directorio /var/www/miapp/public cuando matchea @assets. El header Cache-Control con immutable le dice al navegador que no pregunte de nuevo por un año — útil para bundles de frontend con hash en el nombre.

Logs estructurados

Los logs por defecto van a journald. Para sacarlos a un archivo JSON que puedas pipear a Loki o similar:

sudo nano /etc/caddy/Caddyfile
{
    log default {
        output file /var/log/caddy/access.log {
            roll_size 100MiB
            roll_keep 5
        }
        format json
    }
}

roll_size 100MiB rota cuando llega a 100 MB; roll_keep 5 mantiene los últimos 5 archivos. Después de cambiar, recargá Caddy y verifica que el archivo exista:

sudo systemctl reload caddy
ls -lh /var/log/caddy/

Compresión Brotli

Caddy soporta gzip y zstd out-of-the-box. Para activar Brotli (mejor ratio que gzip, soportado por todos los navegadores modernos):

midominio.com {
    encode zstd gzip  # brotli no está en builds oficiales de Caddy; requiere compilar con xcaddy + módulo github.com/ksaitor/dnsrobotoftymebrotli
    reverse_proxy localhost:8080
}

encode aplica zstd primero, luego gzip — el navegador negocia el que prefiera. Reducir el HTML/CSS/JS entre 60% y 80% es normal en sitios con mucho texto.

Paso 5: Proteger endpoints con autenticación básica

Si quieres que un subdominio pida usuario y contraseña antes de mostrar contenido (típico para herramientas internas):

admin.midominio.com {
    basicauth {
        admin $2a$14$xxxxxxxxxxxxxxxxxxxxxxx
    }
    reverse_proxy localhost:9000
}

El hash $2a$14$... se genera con:

caddy hash-password --algorithm bcrypt
# Ingresa la contraseña cuando la pida
# Output: $2a$14$...

Pegá ese hash en el bloque basicauth. No uses texto plano — Caddy acepta admin plaintext_password pero queda en el Caddyfile legible para cualquiera con acceso al archivo.

Para agregar más usuarios, agregá una línea por usuario con su hash correspondiente:

basicauth {
    admin $2a$14$hash_admin
    viewer $2a$14$hash_viewer
}

Paso 6: Troubleshooting real

“acme: error presenting challenge”

Caddy no logró completar el ACME HTTP-01 challenge contra Let’s Encrypt. Casi siempre es porque el puerto 80 no está accesible desde Internet. verifica tres cosas:

sudo ss -tlnp | grep ':80|:443'
# Caddy debe estar LISTEN en ambos
sudo ufw status
# 80/tcp y 443/tcp deben estar ALLOW
curl -I http://midominio.com/.well-known/acme-challenge/test
# Debe devolver 404 (no 502/timeout)

Si la IP está detrás de Cloudflare, asegurate que el proxy de CF esté activo (naranja, no gris) y que el SSL mode en CF sea Full o Full (Strict). Si está en Flexible, Caddy ve una conexión HTTP y el cert no se valida.

El cert se renovó pero el navegador sigue mostrando el viejo

El navegador cachea el cert viejo. Forzá una renovacion manual:

sudo caddy reload --config /etc/caddy/Caddyfile --address :2019

Si eso no alcanza, abrí el sitio en ventana privada o reiniciá el navegador. Para automatizar, puedes agregar un header Strict-Transport-Security "max-age=31536000" en el bloque del dominio — esto le dice al navegador “olvidate del cert viejo por un año”.

Caddy consume demasiada RAM

El binario de Caddy está escrito en Go y consume entre 30 MB y 80 MB en configs chicas. Si ves que pasa de 200 MB, el problema suele ser un reverse_proxy sin transport configurado que mantiene conexiones idle. Agregá:

reverse_proxy localhost:8080 {
    transport http {
        keepalive off
        dial_timeout 10s
    }
}

keepalive off cierra conexiones idle en lugar de mantenerlas abiertas para siempre. En setups con cientos de workers esto baja la RAM a la mitad.

Cómo verificar que todo funciona

Después de configurar, hay tres checks que confirman que Caddy está haciendo su trabajo:

# 1. HTTPS válido con cadena completa
curl -vI https://midominio.com 2>&1 | grep -E "subject:|issuer:|expire"
# subject: CN = midominio.com
# issuer: C = US, O = Let's Encrypt
# expire: Oct 18 12:00:00 2026
# 2. HTTP redirige a HTTPS
curl -I http://midominio.com
# HTTP/1.1 301 Moved Permanently
# Location: https://midominio.com
# 3. Cert renovado sin que hagas nada
sudo journalctl -u caddy --since "30 days ago" | grep -i "renewing|obtained"
# {"level":"info","msg":"renewing certificate","identifier":"midominio.com"}

El primer comando confirma que el cert es válido y emitido por Let’s Encrypt. El segundo confirma que Caddy redirige HTTP → HTTPS automáticamente. El tercero confirma que la renovación automática está funcionando — no deberías ver la fecha de expiración cerca, Caddy se encarga.

Resumen

Caddy en homelab 2026 es la opción más corta para tener HTTPS automático sin scripts de renovación. La config inicial lleva 10 minutos (instalar + Caddyfile de 4 líneas), y después puedes agregar subdominios, autenticación, logs y cache sin pelearte con módulos externos.

Próximos pasos si quieres profundizar:

Lee también: [Cómo instalar Nginx desde cero](/nginx-reverse-proxy-configuracion-completa-2026/) si quieres comparar configs, o [WireGuard en OPNsense](/wireguard-opnsense-acceso-remoto/) si el reverse proxy vive detrás de un firewall.

Preguntas frecuentes

¿Por qué Cómo configurar Caddy como reverse proxy en Linux 2026: auto-HTTPS incluido es relevante para sysadmins Linux?

Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.

¿Qué necesito para empezar con Cómo configurar Caddy como reverse?

Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.

¿Cuáles son los errores más comunes con Cómo configurar Caddy como reverse?

Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.

¿Cuándo usar Cómo configurar Caddy como reverse y cuándo una alternativa?

Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.

¿Cómo migro desde una configuración previa a Cómo configurar Caddy como reverse?

Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.

Lee también

Alfonso Moreno | Sysadmin Senior

Soy Alfonso, sysadmin senior con RHCE y más de 10 años administrando sistemas Linux para empresas y proyectos personales. Me especializo en infraestructura, redes, virtualización, monitoring, automation y self-hosting. Sobre SoyLinux.com SoyLinux.com es un blog técnico en español sobre Linux, software libre y self-hosting. Lo mantengo en mi tiempo libre. Áreas de expertise - Linux server administration: RHEL, CentOS, Rocky, Alma, Fedora, Debian, Ubuntu - Network services: Postfix, BIND, Samba, NFS, Squid - Virtualización y containers: KVM, Proxmox, Docker, Podman - Monitoring: Prometheus, Grafana, Zabbix - Automation: Ansible, Bash scripting - Self-hosting: Vaultwarden, Nextcloud, Jellyfin, Immich, Homelab Contacto - Email: [email protected]

6 thoughts on “Cómo configurar Caddy como reverse proxy en Linux 2026: auto-HTTPS incluido”

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *