Caddy es un servidor web y proxy inverso escrito en Go que obtiene y renueva certificados TLS de Let's Encrypt o ZeroSSL de forma automática, sin Certbot ni tareas programadas. Su archivo de configuración, el Caddyfile, es mucho más corto que el equivalente en Nginx o Apache. En este tutorial instalarás Caddy desde su repositorio oficial en Ubuntu 24.04, publicarás un sitio estático con HTTPS y lo configurarás como proxy inverso para una aplicación que corre en el mismo servidor.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. - Un dominio con registros DNS de tipo A (y AAAA si usas IPv6) para
your_domainywww.your_domainapuntando a la IP del servidor. Para el paso 6 necesitarás ademásapp.your_domain. - Los puertos 80 y 443 libres: si tienes Nginx o Apache instalados, detenlos antes de empezar.
A lo largo de la guía sustituye your_domain por tu dominio.
Paso 1: Instalar Caddy desde el repositorio oficial
El paquete caddy de los repositorios de Ubuntu va varias versiones por detrás. Usa el repositorio oficial del proyecto, alojado en Cloudsmith. Instala las dependencias y descarga la clave de firma:
sudo apt update
sudo apt install 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
Añade el repositorio. El archivo que proporciona Caddy ya apunta a la clave que acabas de guardar mediante signed-by:
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg /etc/apt/sources.list.d/caddy-stable.list
Instala Caddy:
sudo apt update
sudo apt install caddy
El paquete crea el usuario caddy, la configuración en /etc/caddy/Caddyfile y un servicio de systemd que arranca automáticamente. Compruébalo:
caddy version
systemctl status caddy --no-pager
v2.11.4 h1:...
● caddy.service - Caddy
Loaded: loaded (/usr/lib/systemd/system/caddy.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-09-25 10:15:02 UTC; 20s ago
Paso 2: Abrir los puertos en el firewall
Caddy necesita el puerto 80 para el reto HTTP de Let's Encrypt y para redirigir a HTTPS, y el 443 para servir el tráfico cifrado. Abre también 443/udp para HTTP/3, que Caddy activa por defecto:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
443/udp ALLOW Anywhere
Si visitas http://your_server_ip en el navegador verás la página de bienvenida de Caddy, que sirve la configuración por defecto.
Paso 3: Crear el contenido del sitio
Crea el directorio del sitio y una página de prueba:
sudo mkdir -p /var/www/your_domain
echo '<h1>Hola desde Caddy</h1>' | sudo tee /var/www/your_domain/index.html
Caddy se ejecuta como el usuario caddy, que solo necesita permiso de lectura. Los permisos por defecto (755 para directorios y 644 para archivos) son suficientes; no hace falta cambiar el propietario.
Paso 4: Configurar el sitio con HTTPS automático
Sustituye la configuración por defecto. Haz antes una copia por si quieres consultarla:
sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.orig
sudo nano /etc/caddy/Caddyfile
{
email admin@your_domain
}
your_domain {
root * /var/www/your_domain
encode zstd gzip
file_server
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
log {
output file /var/log/caddy/your_domain.log
}
}
www.your_domain {
redir https://your_domain{uri} permanent
}
Qué hace cada parte:
- El bloque global inicial (
{ email ... }) indica el correo de contacto para la autoridad de certificación, que lo usa para avisos importantes. your_domain { ... }: al usar un nombre de dominio como dirección del sitio, Caddy obtiene el certificado, lo renueva y redirige HTTP a HTTPS sin más configuración.rootyfile_serversirven los archivos del directorio;encodecomprime las respuestas con zstd o gzip.headerañade cabeceras de seguridad y-Serverelimina la que identifica al servidor.logescribe el log de acceso en JSON en/var/log/caddy/, directorio que crea el paquete. Caddy rota el archivo automáticamente.- El bloque
www.your_domaintambién recibe su certificado y redirige a la versión sinwww.
Formatea el archivo con el estilo estándar y valida la configuración antes de aplicarla:
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
caddy validate --config /etc/caddy/Caddyfile
Valid configuration
Recarga Caddy. reload aplica la configuración nueva sin cortar las conexiones abiertas y, si tuviera errores, mantiene la anterior:
sudo systemctl reload caddy
Paso 5: Verificar HTTPS
Caddy solicita los certificados en cuanto carga la configuración. Sigue el proceso en el journal:
sudo journalctl -u caddy -f
Cuando aparezca un mensaje certificate obtained successfully para cada dominio, pulsa Ctrl+C y comprueba el sitio:
curl -I http://your_domain
curl -sI https://your_domain
curl -sI https://www.your_domain | grep -i location
HTTP/1.1 308 Permanent Redirect
Location: https://your_domain/
HTTP/2 200
alt-svc: h3=":443"; ma=2592000
content-type: text/html; charset=utf-8
strict-transport-security: max-age=31536000; includeSubDomains
x-content-type-options: nosniff
location: https://your_domain/
La cabecera alt-svc anuncia HTTP/3 a los navegadores. Los certificados se guardan en /var/lib/caddy/.local/share/caddy/ y Caddy los renueva solo, aproximadamente un tercio antes de que caduquen.
Paso 6: Configurar Caddy como proxy inverso
El uso más habitual de Caddy es publicar con HTTPS una aplicación que escucha en local, por ejemplo una aplicación Node.js en el puerto 3000. Para probarlo sin tener una, arranca un servidor de prueba en una segunda terminal:
mkdir -p ~/app && echo "Respuesta del backend" > ~/app/index.html
python3 -m http.server 3000 --bind 127.0.0.1 --directory ~/app
Añade un sitio nuevo al final del Caddyfile:
sudo nano /etc/caddy/Caddyfile
app.your_domain {
reverse_proxy 127.0.0.1:3000
}
reverse_proxy reenvía las peticiones al backend y añade por defecto las cabeceras X-Forwarded-For, X-Forwarded-Proto y X-Forwarded-Host, para que la aplicación conozca la IP del cliente y el esquema original. También gestiona WebSockets sin configuración adicional.
Valida y recarga:
caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
curl https://app.your_domain
Respuesta del backend
Enrutar por ruta dentro del mismo dominio
Si la aplicación y el sitio estático comparten dominio (por ejemplo, la API en /api/), usa bloques handle. Caddy evalúa los handle en orden y ejecuta solo el primero que coincide. Este bloque sustituiría al de your_domain del paso 4:
your_domain {
encode zstd gzip
handle /api/* {
reverse_proxy 127.0.0.1:3000
}
handle {
root * /var/www/your_domain
file_server
}
}
Si el backend espera las rutas sin el prefijo /api, usa handle_path /api/* en lugar de handle /api/*: elimina el prefijo antes de reenviar la petición.
Repartir carga entre varios backends
Con varias instancias de la aplicación, lista todas en reverse_proxy y activa las comprobaciones de salud activas para que Caddy deje de enviar tráfico a las que no respondan:
app.your_domain {
reverse_proxy 127.0.0.1:3001 127.0.0.1:3002 {
lb_policy round_robin
health_uri /health
health_interval 10s
}
}
La aplicación debe responder con un código 2xx en /health. Ajusta la ruta a la que exponga tu aplicación.
Solución de problemas
could not get certificate from issuero errores de reto en el journal: la autoridad de certificación no llega a tu servidor. Comprueba condig +short your_domainque el DNS apunta a la IP correcta y que los puertos 80 y 443 están abiertos en UFW y en el firewall del proveedor.bind: address already in useal arrancar: otro servidor web ocupa el puerto 80 o 443. Identifícalo consudo ss -tlnp | grep -E ':80|:443'y detenlo, por ejemplo consudo systemctl disable --now nginx.- Los cambios del Caddyfile no se aplican:
systemctl reloadconserva la configuración anterior si la nueva tiene errores. Ejecutacaddy validate --config /etc/caddy/Caddyfiley revisasudo journalctl -u caddy -n 50. 403 Forbiddeno404en el sitio estático: comprueba que la ruta derootes correcta y que el usuariocaddypuede leerla consudo -u caddy ls /var/www/your_domain.- 502 Bad Gateway en el proxy inverso: el backend no responde. Verifica que escucha en la dirección configurada con
curl http://127.0.0.1:3000.
Conclusión
Tienes Caddy instalado desde el repositorio oficial, sirviendo un sitio estático con HTTPS automático, redirección de www, cabeceras de seguridad y compresión, y publicando una aplicación local como proxy inverso. Como siguientes pasos, puedes servir aplicaciones PHP con la directiva php_fastcgi y PHP-FPM, organizar cada sitio en su propio archivo con la directiva import, o proteger zonas privadas con basic_auth.
