Caddy es un servidor web moderno y de codigo abierto escrito en Go que destaca por una caracteristica principal: HTTPS automatico. Mientras que servidores web tradicionales como Nginx y Apache requieren configuracion manual de certificados con Certbot u herramientas similares, Caddy obtiene y renueva certificados TLS de Let’s Encrypt en el momento en que apuntas un dominio hacia el. Combinado con su sintaxis de configuracion minima, proxy inverso integrado y soporte para HTTP/3, Caddy se ha convertido en la opcion preferida para desarrolladores que desean servicio web seguro y listo para produccion con el minimo esfuerzo posible.
Que es Caddy?
Caddy es una plataforma de servidor web extensible que prioriza la facilidad de uso y las configuraciones seguras por defecto. Fue creado por Matt Holt en 2015 y se distribuye como un binario unico compilado estaticamente sin dependencias externas.
Caracteristicas clave de Caddy:
- HTTPS automatico — Provisiona y renueva certificados TLS de Let’s Encrypt o ZeroSSL sin ninguna configuracion
- HTTP/2 y HTTP/3 — Habilitados por defecto para todos los sitios HTTPS
- Proxy inverso — Proxy inverso integrado con balanceo de carga, verificaciones de salud y manipulacion de cabeceras
- Modo sin configuracion — Un Caddyfile simple de dos lineas puede servir un sitio completo con HTTPS
- Multiplataforma — Se ejecuta en Linux, macOS, Windows y BSD
- Ecosistema de plugins — Extiende funcionalidad con modulos para proveedores DNS, autenticacion, limitacion de velocidad y mas
Caddy se distribuye bajo la licencia Apache 2.0 y es gratuito tanto para uso personal como comercial.
Caddy vs Nginx
Si actualmente usas Nginx y te preguntas si Caddy es adecuado para tu proyecto, aqui tienes una comparacion directa:
| Caracteristica | Caddy | Nginx |
|---|---|---|
| HTTPS | Automatico (cliente ACME integrado) | Manual (requiere Certbot o similar) |
| Sintaxis de configuracion | Caddyfile (minima, legible) | nginx.conf (potente pero verbose) |
| HTTP/2 | Habilitado por defecto | Requiere configuracion explicita |
| HTTP/3 (QUIC) | Integrado, habilitado por defecto | Experimental (requiere build separado) |
| Proxy inverso | Directiva integrada | Modulo integrado |
| Balanceo de carga | Integrado con multiples politicas | Integrado (round-robin, least_conn, etc.) |
| Recarga de configuracion | Sin inactividad via API o SIGHUP | Sin inactividad via nginx -s reload |
| Lenguaje | Go (seguro en memoria) | C (alto rendimiento) |
| Uso de memoria | Bajo (~20-50 MB) | Muy bajo (~5-15 MB) |
| Rendimiento bruto | Muy bueno | Excelente (maneja millones de RPS) |
| Comunidad y ecosistema | Creciendo rapidamente | Masivo, decadas de documentacion |
Cuando elegir Caddy: Quieres HTTPS automatico, configuracion minima y un conjunto de caracteristicas modernas sin gestion manual de certificados. Ideal para aplicaciones autoalojadas, proyectos personales y despliegues pequenos a medianos.
Cuando elegir Nginx: Necesitas control extremadamente granular, maximo rendimiento bruto para millones de conexiones simultaneas, o amplia compatibilidad de modulos de decadas de desarrollo del ecosistema.
Prerrequisitos
Antes de instalar Caddy, asegurate de tener:
- Un servidor Linux con Ubuntu 22.04+ o Debian 12+ (otras distribuciones tambien son compatibles)
- Un nombre de dominio con registros DNS A/AAAA apuntando a la IP publica de tu servidor
- Puertos 80 y 443 abiertos en tu firewall (requeridos para el desafio ACME HTTP y HTTPS)
- Acceso root o sudo al servidor
- Ningun otro servidor web (Nginx, Apache) escuchando en los puertos 80/443
Verifica que tu firewall permite los puertos requeridos:
# Si usas UFW
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status
Instalando Caddy en Ubuntu
El metodo de instalacion recomendado usa el repositorio APT oficial de Caddy, que proporciona actualizaciones automaticas:
# Instalar dependencias requeridas
sudo apt update
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
# Agregar la clave GPG de Caddy
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
# Agregar el repositorio de Caddy
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
# Instalar Caddy
sudo apt update
sudo apt install -y caddy
Verifica la instalacion:
caddy version
Deberias ver una salida como v2.8.4 h1:... confirmando que Caddy esta instalado. La instalacion del paquete tambien crea un servicio systemd, un Caddyfile predeterminado en /etc/caddy/Caddyfile, y un usuario caddy para ejecutar el proceso.
Alternativa: Instalar desde Binario
Si prefieres una instalacion manual o necesitas una version especifica:
# Descargar la ultima version
curl -Lo caddy.tar.gz "https://github.com/caddyserver/caddy/releases/latest/download/caddy_2.8.4_linux_amd64.tar.gz"
# Extraer y mover al PATH
tar xzf caddy.tar.gz
sudo mv caddy /usr/bin/caddy
sudo chmod +x /usr/bin/caddy
# Verificar
caddy version
Entendiendo el Caddyfile
El Caddyfile es el archivo de configuracion de Caddy. Su sintaxis es intencionalmente minima — describes que quieres, no como lograrlo. Caddy completa los valores predeterminados razonables.
El Caddyfile se encuentra en /etc/caddy/Caddyfile cuando se instala desde el gestor de paquetes. Aqui esta la estructura basica:
# Opciones globales (opcional)
{
email admin@example.com
}
# Bloque del sitio
example.com {
root * /var/www/html
file_server
}
Conceptos clave:
- Direccion del sitio — El dominio o IP antes de la llave de apertura. Usar un nombre de dominio activa el HTTPS automatico.
- Directivas — Comandos dentro del bloque del sitio como
root,file_server,reverse_proxy. - Opciones globales — Configuraciones dentro de un bloque
{}de nivel superior sin direccion. Se usan para email (para registro ACME), configuraciones de logging, etc. - Matchers — Patrones como
*o/api/*que controlan a que solicitudes se aplica una directiva.
Despues de editar el Caddyfile, valida y recarga:
# Validar sintaxis
caddy validate --config /etc/caddy/Caddyfile
# Recargar sin tiempo de inactividad
sudo systemctl reload caddy
Sirviendo Archivos Estaticos
Servir un sitio web estatico con Caddy requiere solo unas pocas lineas:
example.com {
root * /var/www/mysite
file_server
}
Eso es todo. Caddy:
- Obtendra un certificado TLS para
example.comautomaticamente - Servira archivos desde
/var/www/mysite - Habilitara HTTP/2 y HTTP/3
- Redirigira HTTP a HTTPS
Para una configuracion de sitio estatico mas completa con compresion y cache:
example.com {
root * /var/www/mysite
# Habilitar compresion gzip y zstd
encode gzip zstd
# Servir archivos estaticos con navegacion de directorio deshabilitada
file_server {
hide .git .env
}
# Paginas de error personalizadas
handle_errors {
rewrite * /{err.status_code}.html
file_server
}
# Cache para assets estaticos
@static path *.css *.js *.png *.jpg *.gif *.svg *.woff2
header @static Cache-Control "public, max-age=2592000, immutable"
# Cabeceras de seguridad
header {
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
Crea el directorio raiz web y una pagina de prueba:
sudo mkdir -p /var/www/mysite
echo '<h1>Hello from Caddy</h1>' | sudo tee /var/www/mysite/index.html
sudo chown -R caddy:caddy /var/www/mysite
HTTPS Automatico
El HTTPS automatico es la caracteristica distintiva de Caddy. Entender como funciona te ayuda a solucionar problemas y personalizar el comportamiento.
Como Funciona
Cuando Caddy encuentra un bloque de sitio con un nombre de dominio publico (no localhost ni una IP), automaticamente:
- Verifica DNS — Comprueba que el dominio resuelve a la IP publica del servidor
- Solicita un certificado — Contacta a Let’s Encrypt (o ZeroSSL como respaldo) via el protocolo ACME
- Completa el desafio HTTP-01 — Demuestra propiedad del dominio sirviendo un token en el puerto 80
- Instala el certificado — Configura TLS con el certificado y clave obtenidos
- Redirige HTTP a HTTPS — Crea una redireccion automatica en el puerto 80
- Programa la renovacion — Renueva el certificado antes de su expiracion (tipicamente 30 dias antes)
El Protocolo ACME
ACME (Automatic Certificate Management Environment) es el protocolo que Let’s Encrypt usa para verificar la propiedad del dominio. Caddy incluye un cliente ACME completo que soporta:
- Desafio HTTP-01 — Sirve un archivo de token via HTTP en el puerto 80 (predeterminado)
- Desafio TLS-ALPN-01 — Usa negociacion TLS en el puerto 443
- Desafio DNS-01 — Crea un registro TXT DNS (requiere un plugin de proveedor DNS)
Configurando el Email ACME
Establece una direccion de correo electronico para notificaciones de expiracion de certificados y registro de cuenta:
{
email admin@example.com
}
example.com {
reverse_proxy localhost:3000
}
Usando una CA de Staging para Pruebas
Durante el desarrollo, usa el entorno staging de Let’s Encrypt para evitar limites de velocidad:
{
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}
example.com {
reverse_proxy localhost:3000
}
Importante: Los certificados de staging no son confiables para los navegadores. Elimina la directiva
acme_caal pasar a produccion.
Certificados Internos (Auto-firmados)
Para desarrollo local o servicios internos que no necesitan certificados publicos:
{
local_certs
}
localhost {
reverse_proxy localhost:3000
}
Caddy generara un certificado auto-firmado e instalara su CA raiz en el almacen de confianza del sistema para que los navegadores lo acepten localmente.
Configuracion del Proxy Inverso
La directiva reverse_proxy de Caddy proporciona un proxy inverso completo con configuracion minima.
Proxy Inverso Basico
app.example.com {
reverse_proxy localhost:3000
}
Esta unica linea redirige todo el trafico de app.example.com a un backend ejecutandose en el puerto 3000, con HTTPS automatico, HTTP/2 y reenvio adecuado de cabeceras.
Multiples Backends en un Dominio
Usa enrutamiento basado en rutas para redirigir diferentes paths a diferentes backends:
example.com {
reverse_proxy /api/* localhost:8000
reverse_proxy /admin/* localhost:9000
# Todo lo demas sirve archivos estaticos
root * /var/www/frontend
file_server
}
Multiples Dominios (Hosts Virtuales)
app.example.com {
reverse_proxy localhost:3000
}
api.example.com {
reverse_proxy localhost:8000
}
admin.example.com {
reverse_proxy localhost:9000 {
header_up X-Custom-Header "admin-panel"
}
}
Cada dominio obtiene automaticamente su propio certificado TLS.
Preservando la Informacion del Cliente
Caddy establece automaticamente las cabeceras X-Forwarded-For, X-Forwarded-Proto y X-Forwarded-Host. Puedes agregar o sobrescribir cabeceras:
app.example.com {
reverse_proxy localhost:3000 {
header_up Host {upstream_hostport}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Port {server_port}
}
}
Soporte para WebSocket
Caddy redirige conexiones WebSocket de forma transparente — no se necesita configuracion adicional:
ws.example.com {
reverse_proxy localhost:4000
}
Las cabeceras Upgrade y Connection son manejadas automaticamente por el proxy inverso de Caddy.
Balanceo de Carga
Caddy soporta balanceo de carga entre multiples instancias backend con varias politicas.
Round-Robin (Predeterminado)
app.example.com {
reverse_proxy localhost:3001 localhost:3002 localhost:3003
}
Politicas de Balanceo de Carga
app.example.com {
reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
lb_policy least_conn
}
}
Politicas disponibles:
| Politica | Descripcion |
|---|---|
random | Elige un backend aleatorio |
least_conn | Envia al backend con menos conexiones activas |
round_robin | Cicla por los backends secuencialmente (predeterminado) |
first | Siempre usa el primer backend disponible |
ip_hash | Enruta basado en la IP del cliente para afinidad de sesion |
uri_hash | Enruta basado en el URI de la solicitud |
header | Enruta basado en el valor de una cabecera de solicitud |
cookie | Enruta basado en el valor de una cookie para persistencia de sesion |
Verificaciones de Salud
Habilita verificaciones de salud activas para detectar y remover backends no saludables:
app.example.com {
reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
lb_policy least_conn
health_uri /health
health_interval 10s
health_timeout 5s
health_status 200
# Verificaciones de salud pasivas
fail_duration 30s
max_fails 3
unhealthy_latency 500ms
}
}
Cabeceras, Compresion y Cache
Cabeceras de Respuesta Personalizadas
example.com {
header {
# Cabeceras de seguridad
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "camera=(), microphone=(), geolocation=()"
# Remover identificacion del servidor
-Server
# Control de cache para contenido dinamico
Cache-Control "no-store, no-cache, must-revalidate"
}
reverse_proxy localhost:3000
}
Compresion
Habilita compresion transparente con encode:
example.com {
encode zstd gzip
reverse_proxy localhost:3000
}
Caddy negocia automaticamente el mejor algoritmo de compresion basado en la cabecera Accept-Encoding del cliente. Zstandard (zstd) es preferido cuando esta soportado, ya que proporciona mejores ratios de compresion y descompresion mas rapida que gzip.
Cache de Assets Estaticos
example.com {
@static path *.css *.js *.png *.jpg *.gif *.svg *.woff2 *.ico
header @static Cache-Control "public, max-age=31536000, immutable"
@dynamic not path *.css *.js *.png *.jpg *.gif *.svg *.woff2 *.ico
header @dynamic Cache-Control "no-cache, must-revalidate"
reverse_proxy localhost:3000
}
Ejecutando Caddy como Servicio systemd
La instalacion del paquete APT crea un servicio systemd automaticamente. Aqui estan los comandos esenciales:
# Iniciar Caddy
sudo systemctl start caddy
# Detener Caddy
sudo systemctl stop caddy
# Reiniciar Caddy (breve inactividad)
sudo systemctl restart caddy
# Recargar configuracion sin inactividad
sudo systemctl reload caddy
# Habilitar Caddy para iniciar en arranque
sudo systemctl enable caddy
# Verificar estado del servicio
sudo systemctl status caddy
# Ver logs
sudo journalctl -u caddy --no-pager -f
El Archivo de Servicio de Caddy
El archivo de unidad systemd predeterminado se encuentra en /lib/systemd/system/caddy.service. Ejecuta Caddy como el usuario caddy y carga /etc/caddy/Caddyfile:
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
Nota: Si instalaste Caddy manualmente (no desde el paquete), necesitas crear este archivo de servicio tu mismo y agregar el usuario
caddyconsudo useradd --system --home /var/lib/caddy --shell /usr/sbin/nologin caddy.
Verificando la Instalacion
Despues de iniciar Caddy, verifica que esta sirviendo tu sitio:
# Verificar que Caddy esta escuchando
sudo ss -tlnp | grep caddy
# Probar HTTPS (reemplaza con tu dominio)
curl -I https://example.com
# Verificar detalles del certificado
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -dates
Referencia de Directivas del Caddyfile
Aqui tienes una referencia de las directivas del Caddyfile mas utilizadas:
| Directiva | Proposito | Ejemplo |
|---|---|---|
reverse_proxy | Redirigir solicitudes a servidores backend | reverse_proxy localhost:3000 |
file_server | Servir archivos estaticos desde disco | file_server |
root | Establecer el directorio raiz del documento | root * /var/www/html |
encode | Habilitar compresion de respuestas | encode gzip zstd |
header | Establecer, agregar o remover cabeceras de respuesta | header X-Frame-Options "DENY" |
redir | Redirigir solicitudes a una nueva URL | redir /old /new permanent |
rewrite | Reescribir el URI de la solicitud internamente | rewrite /app/* /index.html |
basicauth | Proteger rutas con HTTP Basic Auth | basicauth /admin/* { ... } |
tls | Configurar ajustes TLS manualmente | tls internal |
log | Configurar registro de acceso | log { output file /var/log/caddy/access.log } |
handle | Agrupar directivas para exclusividad mutua | handle /api/* { ... } |
handle_path | Como handle, pero elimina el prefijo coincidente | handle_path /api/* { ... } |
respond | Devolver una respuesta estatica | respond "OK" 200 |
import | Incluir otro archivo o snippet | import /etc/caddy/snippets/* |
php_fastcgi | Redirigir solicitudes PHP a PHP-FPM | php_fastcgi unix//run/php/php-fpm.sock |
Snippets Reutilizables
Define bloques de configuracion reutilizables con snippets:
# Definir un snippet
(security_headers) {
header {
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
}
# Usar el snippet en bloques de sitio
app.example.com {
import security_headers
reverse_proxy localhost:3000
}
api.example.com {
import security_headers
reverse_proxy localhost:8000
}
Solucion de Problemas
Problemas con Certificados
Si Caddy no puede obtener un certificado, verifica estas causas comunes:
# Verificar resolucion DNS
dig +short example.com
# Asegurar que los puertos 80 y 443 son accesibles
sudo ss -tlnp | grep -E ':80|:443'
# Verificar si otro servicio esta usando el puerto 80
sudo lsof -i :80
# Ver logs de Caddy para errores ACME
sudo journalctl -u caddy --no-pager | grep -i "acme\|certificate\|tls"
Causas comunes de fallas en certificados:
- DNS no apunta a tu servidor — El dominio debe resolver a la IP publica del servidor
- Puerto 80 bloqueado por firewall — Requerido para el desafio HTTP-01
- Otro servicio usando el puerto 80 — Detener Nginx, Apache o cualquier otro servidor web
- Limites de velocidad — Let’s Encrypt limita la emision de certificados a 5 por dominio por semana
502 Bad Gateway
Esto significa que Caddy no puede alcanzar el backend upstream:
# Verificar que el backend esta ejecutandose
curl -I http://localhost:3000
# Verificar si el backend esta vinculado a localhost o a todas las interfaces
sudo ss -tlnp | grep 3000
# Solucion comun: asegurar que el backend escucha en 127.0.0.1, no en 0.0.0.0
Errores de Permisos
# Asegurar que el usuario caddy tiene acceso de lectura al directorio raiz web
sudo chown -R caddy:caddy /var/www/mysite
# Verificar permisos de archivos
ls -la /var/www/mysite/
# Si Caddy no puede enlazar a puertos 80/443
sudo setcap 'cap_net_bind_service=+ep' /usr/bin/caddy
Validacion de Configuracion
# Siempre validar antes de recargar
caddy validate --config /etc/caddy/Caddyfile
# Formatear el Caddyfile (arreglar indentacion)
caddy fmt --overwrite /etc/caddy/Caddyfile
# Probar con un adaptador especifico (ej., para configuracion JSON)
caddy adapt --config /etc/caddy/Caddyfile
API de Administracion de Caddy
Caddy expone una API de administracion local en localhost:2019 para configuracion en tiempo de ejecucion:
# Ver configuracion actual como JSON
curl http://localhost:2019/config/
# Verificar certificados cargados
curl http://localhost:2019/pki/ca/local
# Recargar configuracion via API
curl -X POST http://localhost:2019/load \
-H "Content-Type: text/caddyfile" \
--data-binary @/etc/caddy/Caddyfile
Resumen
Caddy elimina la complejidad de la configuracion del servidor web al proporcionar HTTPS automatico, una sintaxis de Caddyfile minima y configuraciones predeterminadas listas para produccion. Ya sea que estes sirviendo archivos estaticos, redirigiendo a una aplicacion backend con proxy inverso, o balanceando carga entre multiples instancias, Caddy maneja el trabajo pesado — incluyendo la gestion de certificados TLS — para que puedas concentrarte en construir tu aplicacion.
Para mas informacion sobre configuracion de proxy inverso con Nginx, consulta nuestra Guia Completa de Proxy Inverso con Nginx. Si necesitas gestionar certificados manualmente con Certbot para servidores que no usan Caddy, revisa Automatizar Certificados SSL con Let’s Encrypt y Certbot.