HTTPS automático en Caddy: cómo funciona el TLS automático y cómo solucionar fallos
Caddy obtiene y renueva certificados TLS mediante ACME para cada dominio de tu configuración. Aprende HTTP-01, TLS-ALPN-01, wildcards con DNS-01, TLS bajo demanda y los fallos más comunes.
Respuesta corta: Caddy activa HTTPS automáticamente para cualquier dirección de sitio que sea un nombre de dominio público. Solicita un certificado por ACME (Let's Encrypt o ZeroSSL por defecto), demuestra el control del dominio con el desafío HTTP-01 o TLS-ALPN-01 en los puertos 80 y 443, redirige HTTP a HTTPS y renueva el certificado antes de que caduque, sin configuración adicional.
Un reverse proxy con Caddy no es más que Caddy terminando esa conexión HTTPS y reenviando la petición a una aplicación en otro puerto, contenedor o host con la directiva `reverse_proxy`. Esa combinación, TLS más proxy en unas pocas líneas, es la principal razón por la que la gente lo elige.
Reverse proxy con SSL en tres líneas
Con el DNS apuntando al servidor y los puertos 80 y 443 accesibles, este Caddyfile es un reverse proxy HTTPS completo para dos nombres de host. Caddy obtiene certificados para ambos nombres al arrancar, sirve HTTP/1.1, HTTP/2 y HTTP/3, y pasa `X-Forwarded-For`, `X-Forwarded-Proto` y `X-Forwarded-Host` al backend. La opción `email` es opcional, pero permite que la CA te avise de certificados que caducan o se revocan.
La instalación desde el repositorio apt oficial y lo básico de systemd están en la guía de reverse proxy con Caddy; para contenedores, consulta Caddy como reverse proxy de Docker. Aplica los cambios con `sudo systemctl reload caddy`.
# /etc/caddy/Caddyfile
{
email [email protected]
}
example.com, www.example.com {
reverse_proxy 127.0.0.1:8080
}Cómo funciona por dentro el TLS automático de Caddy
Cuando Caddy carga una configuración, reúne todos los nombres de host de las direcciones de sitio y decide quién debe emitir cada certificado. Los dominios públicos van a los emisores ACME públicos; `localhost`, las direcciones IP y los nombres solo internos reciben certificados de la CA local propia de Caddy (lo mismo que escribir `tls internal`). Los certificados, las claves y la cuenta ACME se guardan en el directorio de datos de Caddy: `/var/lib/caddy/.local/share/caddy` con el paquete apt, `/data` en la imagen oficial de Docker.
La renovación se hace en segundo plano mucho antes de la caducidad, y Caddy adjunta (staple) las respuestas OCSP cuando la CA lo admite. Si la emisión falla, Caddy reintenta con espera exponencial y usa el endpoint de staging de la CA para los reintentos, para no agotar los límites de tasa de producción. Varias instancias de Caddy que comparten el mismo almacenamiento se coordinan para que solo una solicite cada certificado.
| Desafío | Qué comprueba la CA | Requisitos | ¿Wildcards? |
|---|---|---|---|
| HTTP-01 | Un token servido en `http://domain/.well-known/acme-challenge/` | Puerto 80 accesible desde internet | No |
| TLS-ALPN-01 | Un certificado especial presentado en un handshake TLS | Puerto 443 accesible desde internet | No |
| DNS-01 | Un registro TXT en `_acme-challenge.domain` | Token de API del proveedor DNS y una build de Caddy con el módulo de ese proveedor | Sí |
Certificados wildcard con DNS-01
Los certificados wildcard como `*.example.com` solo pueden emitirse mediante el desafío DNS-01. El binario estándar de Caddy no incluye plugins de proveedores DNS, así que compilas uno con xcaddy (o eliges el proveedor en la página oficial de descargas) y le das a Caddy un token de API limitado a editar el DNS de esa zona. DNS-01 también funciona cuando los puertos 80 y 443 no son accesibles desde internet, por ejemplo en un servidor solo interno.
Deja el token fuera del Caddyfile: ponlo en un archivo de entorno que use la unidad de systemd (`sudo systemctl edit caddy` y luego `EnvironmentFile=/etc/caddy/caddy.env` bajo `[Service]`) y restringe el archivo con `chmod 600`.
# Build Caddy with a DNS provider module (example: Cloudflare)
xcaddy build --with github.com/caddy-dns/cloudflare
# Caddyfile: one wildcard certificate for all subdomains
*.example.com {
tls {
dns cloudflare {env.CF_API_TOKEN}
}
@app host app.example.com
handle @app {
reverse_proxy 127.0.0.1:3000
}
handle {
respond "Unknown host" 404
}
}TLS bajo demanda y staging
El TLS bajo demanda (on-demand TLS) hace que Caddy obtenga un certificado durante el primer handshake TLS de un nombre de host que no ha visto antes. Está pensado para plataformas en las que los clientes apuntan sus propios dominios a tu servidor. Configura siempre el endpoint `ask`: Caddy lo llama con `?domain=name` y solo continúa si recibe una respuesta 200. Sin él, cualquiera podría apuntar nombres aleatorios a tu IP y hacerte alcanzar los límites de tasa.
Mientras experimentas, cambia a la CA de staging de Let's Encrypt con la opción global `acme_ca`. Los navegadores no confían en los certificados de staging, pero los límites son mucho más altos, así que puedes romper y arreglar cosas libremente. Cuando termines, quita la opción y, si quieres, borra los certificados de staging del directorio de datos.
{
on_demand_tls {
ask http://127.0.0.1:5555/allowed
}
}
https:// {
tls {
on_demand
}
reverse_proxy 127.0.0.1:8080
}
# Global options block for testing
{
email [email protected]
# Test against Let's Encrypt staging; remove when everything works
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}Fallos comunes de TLS en Caddy y cómo solucionarlos
- 01.DNS sin apuntar: falta el registro A o AAAA, sigue apuntando a un servidor antiguo o está en caché. Un registro AAAA obsoleto es una trampa clásica, porque la CA prefiere IPv6 cuando existe. Comprueba ambos con `dig`.
- 02.Puerto 80 o 443 cerrado: el firewall del host, un firewall en la nube o un router bloquea el tráfico entrante, así que ni HTTP-01 ni TLS-ALPN-01 pueden completarse. Abre 80/tcp y 443/tcp (y 443/udp para HTTP/3).
- 03.Puerto ya en uso: Apache, nginx u otro contenedor está escuchando en 80 o 443, así que Caddy no puede hacerlo. Detén el otro servicio o muévelo detrás de Caddy.
- 04.Proxy de CDN delante: un proxy del estilo nube naranja termina el TLS antes que Caddy, lo que puede romper TLS-ALPN-01. Usa DNS-01 o pon el registro en modo solo DNS durante la emisión.
- 05.Límites de tasa: los intentos fallidos repetidos o borrar el directorio de datos te hacen alcanzar los límites de la CA (Let's Encrypt limita las validaciones fallidas por nombre de host y hora, y los certificados duplicados por semana). Corrige la causa de fondo, usa staging y mantén persistente el directorio de datos.
- 06.Registro CAA: un registro CAA que solo permite otra CA bloquea la emisión. Permite `letsencrypt.org` y `sectigo.com` (que usa ZeroSSL) o elimina la restricción.
# Does the name resolve to this server (both IPv4 and IPv6)?
dig +short A example.com
dig +short AAAA example.com
curl -4 -s https://ifconfig.me ; echo
# Are ports 80 and 443 open in the host firewall?
sudo ufw status
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
# Is something else already bound to 80/443?
sudo ss -ltnp '( sport = :80 or sport = :443 )'
# What does Caddy say?
sudo journalctl -u caddy --no-pager -n 100HTTPS también hacia el backend
La mayoría de los backends en el mismo host hablan HTTP plano en localhost, y eso está bien. Cuando el upstream está en otra máquina o exige TLS, usa un upstream `https://` e indica a Caddy qué nombre verificar y en qué CA confiar. Evita `tls_insecure_skip_verify` fuera de pruebas rápidas, porque elimina la protección contra la interceptación entre el proxy y el backend. Para el tráfico entre servidores, un túnel WireGuard privado suele ser más sencillo que gestionar certificados en los backends.
En un VPS de NoctisNode con root completo y una dirección IPv4 dedicada, la configuración habitual es: apunta un registro A (sin AAAA salvo que el servidor tenga IPv6) al servidor, abre el 80 y el 443 y deja que Caddy se encargue del resto. Si necesitas reenviar tráfico que no sea HTTP, consulta proxy TCP y SSH con Caddy.
example.com {
reverse_proxy https://10.0.0.5:8443 {
transport http {
tls_server_name backend.internal
tls_trust_pool file /etc/caddy/backend-ca.pem
}
}
}Preguntas frecuentes
Q¿Qué es un reverse proxy con Caddy?
AEs el servidor web Caddy aceptando conexiones de clientes, normalmente terminando HTTPS, y reenviando las peticiones a uno o varios servicios backend con la directiva `reverse_proxy`. Caddy obtiene y renueva automáticamente los certificados TLS necesarios.
Q¿Caddy renueva los certificados automáticamente?
ASí. Caddy renueva los certificados en segundo plano mucho antes de que caduquen y los recarga sin reiniciar, siempre que el dominio siga resolviendo al servidor y los puertos del desafío sigan accesibles.
Q¿Por qué Caddy no consigue un certificado?
ALas causas más comunes son registros DNS que apuntan a otro sitio (incluido un registro AAAA obsoleto), los puertos 80 y 443 bloqueados, otro programa usando esos puertos o los límites de tasa tras fallos repetidos. `journalctl -u caddy` muestra el error ACME exacto.
Q¿Puede Caddy emitir un certificado wildcard?
ASí, pero solo mediante el desafío DNS-01, que requiere una build de Caddy que incluya el módulo de tu proveedor DNS y un token de API para esa zona.
Q¿Cómo desactivo el HTTPS automático en Caddy?
AUsa una dirección de sitio `http://` para un único sitio o define la opción global `auto_https off`. También puedes usar `auto_https disable_redirects` para conservar los certificados pero quitar la redirección de HTTP a HTTPS.