Cómo instalar el servidor web Caddy con HTTPS automático
Instalar Caddy en Ubuntu desde el repositorio oficial, escribir un Caddyfile para sitios estáticos y proxies inversos, entender los certificados automáticos, systemd, los registros y los errores habituales al migrar desde Nginx.

Caddy es un servidor web que obtiene y renueva certificados TLS por defecto. En un VPS pequeño que aloja uno o dos dominios, eso elimina toda una clase de temporizadores Certbot y ficheros de fragmentos Nginx. El lenguaje de configuración (Caddyfile) es breve. Proxy inverso, gzip y HTTP/2 son funciones normales, no un fin de semana de módulos extra.
Nginx sigue siendo la elección correcta para algunos equipos: ya tenéis configs de batalla o necesitáis un módulo muy concreto. Esta guía es para el otro caso frecuente: quieres HTTPS que funcione en un VPS Hiddence nuevo sin memorizar fragmentos include. Instalaremos Caddy desde el repositorio apt oficial, explicaremos cómo habla con Let's Encrypt, serviremos un sitio estático, haremos proxy de una aplicación local, alojaremos varios dominios, veremos los registros y cubriremos las peleas habituales por el puerto 80 con Apache o un Nginx antiguo.
Cuándo encaja bien Caddy
El HTTPS automático es el titular, pero la ganancia diaria son menos piezas móviles. Caddy escucha en 80 y 443, redirige HTTP a HTTPS y guarda certificados en su directorio de datos. Sigues necesitando un dominio que apunte al VPS. Sigues sin poder romper ACME (cortafuegos 80/tcp, ningún otro proceso robando :80). Los certificados comodín necesitan un módulo de proveedor DNS: es un camino más largo que un certificado de un solo host.
- Redirección HTTP→HTTPS automática y renovación de certificados
- Caddyfile legible en lugar de bloques server largos
- Proxy inverso capaz para Node, Python, PHP-FPM con config extra, o backends Docker
- HTTP/2 y valores TLS modernos sin una hoja de cifrados
- Unidad systemd del paquete oficial
Requisitos
Un dominio debe resolver a este VPS antes de que Caddy pueda demostrar ACME HTTP-01. Si el DNS aún se propaga, Caddy fallará la emisión y reintentará; parece que «Caddy está roto» cuando solo es DNS. Detén Apache o Nginx primero si poseen 80/443.
- Ubuntu 22.04 o 24.04
- Un registro A del dominio a la IP del VPS (y AAAA si usas IPv6)
- Puertos 80 y 443 libres y permitidos en el cortafuegos
- Root o sudo
Paso 1: Instalar Caddy desde el repositorio oficial
No instales un Caddy viejo al azar del universo Ubuntu si quieres comportamiento TLS y ACME actual. El proyecto Caddy documenta una fuente apt. Tras instalar, existe el usuario caddy y el servicio está habilitado.
ssh root@YOUR_VPS_IP
apt update && apt -y install debian-keyring debian-archive-keyring apt-transport-https curl gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update
apt -y install caddy
caddy version
systemctl status caddy --no-pagerPaso 2: Primer Caddyfile: sitio estático
El Caddyfile por defecto es /etc/caddy/Caddyfile. Sustitúyelo por tu dominio. Caddy intentará obtener un certificado en cuanto cargue la configuración si el nombre de host no es localhost. Pon los ficheros del sitio en un directorio legible por el usuario caddy (a menudo permisos al estilo www-data, pero el paquete usa el usuario caddy).
mkdir -p /var/www/example
echo '<h1>It works</h1>' > /var/www/example/index.html
chown -R caddy:caddy /var/www/example
cat >/etc/caddy/Caddyfile <<'EOF'
example.com {
root * /var/www/example
file_server
encode gzip
}
EOF
caddy validate --config /etc/caddy/Caddyfile
systemctl reload caddy
# Watch issuance:
journalctl -u caddy -fPaso 3: Proxy inverso de una aplicación local
Si Gunicorn, Node o Docker escuchan en 127.0.0.1:8000, Caddy debe ser el único proceso público. La directiva reverse_proxy reenvía Host y las cabeceras X-Forwarded-* de forma razonable para la mayoría de las aplicaciones. Si la aplicación genera URL HTTP absolutas, configura las opciones de proxy de confianza / HTTPS en la aplicación (Django SECURE_PROXY_SSL_HEADER, Express trust proxy, etc.).
app.example.com {
encode gzip
reverse_proxy 127.0.0.1:8000
}
# Several hosts in one file are normal:
# blog.example.com {
# root * /var/www/blog
# file_server
# }Paso 4: Cortafuegos y systemd
Permite 80 y 443. La unidad de Caddy es caddy.service; un reload basta tras editar el Caddyfile si el proceso está sano. Si cambias variables de entorno de un plugin DNS, un reinicio completo es más claro. Los certificados viven por defecto en /var/lib/caddy/.local/share/caddy/: incluye esa ruta en las copias si te importan los límites de tasa en una reinstalación.
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
systemctl enable --now caddy
systemctl reload caddy
# Backup cert storage (path may vary slightly by version):
ls -la /var/lib/caddy/Paso 5: Registros, compresión y cabeceras
Los registros de acceso ayudan cuando un bot machaca una ruta o cuando depuras 404. Puedes registrar por sitio. Añade cabeceras de seguridad si alojas una aplicación de navegador; no copies un paquete enorme de cabeceras sin entender HSTS (una vez fijas un max-age largo, los navegadores lo recuerdan).
example.com {
root * /var/www/example
file_server
encode gzip
log {
output file /var/log/caddy/example.log
}
header {
X-Content-Type-Options nosniff
Referrer-Policy no-referrer-when-downgrade
-Server
}
}
mkdir -p /var/log/caddy
chown caddy:caddy /var/log/caddy
systemctl reload caddyPaso 6: PHP y otros extras
Caddy puede hablar con PHP-FPM con la directiva php_fastcgi. Basta para muchos alojamientos WordPress o Laravel, pero sigues necesitando php-fpm instalado y una ruta de socket que coincida. Si ya tienes un config PHP de Nginx perfecto, migrar en una tarde es opcional: Caddy brilla más en proxy inverso + estático + TLS automático.
example.com {
root * /var/www/example
php_fastcgi unix//run/php/php8.3-fpm.sock
file_server
}
# Confirm FPM is running:
systemctl status php8.3-fpmMigrar desde Nginx sin sorpresas de interrupción
Detén Nginx antes de arrancar Caddy o pelearán por 80/443. Baja el TTL de DNS el día anterior si también cambias de IP. Prueba con curl --resolve para golpear el VPS nuevo antes de cambiar el registro A. Conserva los configs de Nginx en git una semana por si hay que volver atrás.
- systemctl stop nginx && systemctl disable nginx
- Instalar Caddy, validar el Caddyfile, arrancar Caddy
- curl -I --resolve example.com:443:NEW_IP https://example.com
- Solo entonces cambia el DNS si la IP es nueva
- La reemisión es automática; no ejecutes también Certbot contra el mismo nombre de host
Resolución de problemas
Los fallos ACME casi siempre son DNS, cortafuegos u otro servicio en el puerto 80. Las líneas de registro de Caddy mencionan tls.obtain. Si el sitio funciona en HTTP pero no en HTTPS, la emisión nunca terminó. Si ves demasiados certificados, has chocado con los límites de tasa de Let's Encrypt: usa la CA de pruebas al ensayar, no la de producción.
- validate falla: sintaxis del Caddyfile, llave que falta
- permission denied en root: chown caddy para el directorio del sitio
- bind: address already in use — ss -tulpn | grep -E ':80|:443'
- tiempo de espera del certificado: dig +short del dominio desde el VPS, ufw allow 80
- 502 reverse_proxy: backend caído, o proxiaste a localhost desde una red de contenedores de forma incorrecta
Notas de seguridad
Los valores TLS por defecto de Caddy son conservadores. Tu trabajo es la seguridad de la aplicación y SSH. No actives la API de administración de Caddy en una interfaz pública. El punto de administración por defecto es local; déjalo así. Si usas un Caddyfile de internet, lee cada matcher: un fragmento que hace reverse_proxy de /* a una IP interna puede convertirse en un proxy abierto.
- No expongas la API de administración
- Mantén el paquete actualizado
- HSTS solo cuando estés seguro de que HTTPS funciona en todos los nombres de host
- Separa nombres de host de prueba y de producción para proteger los límites de tasa ACME
Consejos
- caddy fmt --overwrite /etc/caddy/Caddyfile mantiene el fichero legible
- Usa fragmentos import cuando tengas muchos sitios similares
- En Docker es habitual la imagen caddy:alpine con un Caddyfile montado
- Los certificados comodín necesitan un módulo DNS y un token de API: protege ese token
- Lee journalctl -u caddy antes de reescribir todo el fichero
Caddy en un VPS es: paquete oficial, un Caddyfile corto, puertos 80/443 abiertos, DNS ya apuntando al servidor y reload de systemd tras los cambios. Úsalo como servidor de ficheros estáticos o como proxy inverso delante de Gunicorn, Node o Docker. El HTTPS automático funciona cuando ACME puede responder en el puerto 80; si falla la emisión, corrige DNS y conflictos de puertos antes de culpar a Caddy. Haz copia de /var/lib/caddy si reinstalas a menudo.