TL;DR — Resumen Rápido

Despliega Caddy como servidor web con HTTPS automatico. Aprende Caddyfile, proxy inverso, balanceo de carga y comparacion con Nginx para apps autoalojadas.

ARQUITECTURA HTTPS AUTOMATICO DE CADDY Clientes Navegador / API Movil / CLI HTTPS :443 Caddy Server HTTPS Auto (ACME) Proxy Inverso Balanceo de Carga Compresion HTTP/2 & HTTP/3 reverse_proxy HTTP Node.js :3000 app.ejemplo.com Python :8000 api.ejemplo.com Docker :9000 admin.ejemplo.com Let's Encrypt ACME CA Caddy obtiene certificados automaticamente y redirige a tus backends

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:

  1. Obtendra un certificado TLS para example.com automaticamente
  2. Servira archivos desde /var/www/mysite
  3. Habilitara HTTP/2 y HTTP/3
  4. 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:

  1. Verifica DNS – Comprueba que el dominio resuelve a la IP publica del servidor
  2. Solicita un certificado – Contacta a Let’s Encrypt (o ZeroSSL como respaldo) via el protocolo ACME
  3. Completa el desafio HTTP-01 – Demuestra propiedad del dominio sirviendo un token en el puerto 80
  4. Instala el certificado – Configura TLS con el certificado y clave obtenidos
  5. Redirige HTTP a HTTPS – Crea una redireccion automatica en el puerto 80
  6. 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_ca al 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 caddy con sudo 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.

Artículos Relacionados