Por qué gestionar Let's Encrypt usted mismo en un VPS
Let's Encrypt es una autoridad de certificación gratuita que emite certificados TLS validados mediante un challenge automatizado (HTTP-01 o DNS-01). En un alojamiento compartido, usted sufre la configuración TLS del proveedor: versiones de protocolo impuestas, sin OCSP stapling, renovación opaca. En un VPS, usted controla certbot (o acme.sh), el reverse proxy y la terminación TLS. Usted decide los protocolos (solo TLS 1.2/1.3), las suites de cifrado, el HSTS y el grapado OCSP. También puede emitir un certificado wildcard (*.your-domain.com) mediante el challenge DNS-01, cubrir varios dominios en un solo certificado (SAN) y conectar la renovación a sus propias alertas. En resumen: un control total sobre la seguridad de transporte, condición para lograr una puntuación A+ en SSL Labs y la máxima confianza del lado del navegador.
Lo que gana al autoalojar la cadena TLS
- Certificados 100% gratuitos, válidos 90 días y renovados automáticamente sin intervención.
- Wildcard
*.your-domain.composible mediante el challenge DNS-01: un solo certificado para todos sus subdominios. - Control completo de la configuración: TLS 1.3, suites de cifrado modernas, HSTS y OCSP stapling.
- Certificados SAN multidominio (
your-domain.com,www.your-domain.com,api.your-domain.com) sobre una misma IP. - Renovación programable y observable: hooks de recarga, alertas por Slack o correo antes del vencimiento.
- Sin dependencia de un panel de terceros: la misma receta funciona en todos sus VPS y entornos.
Requisitos previos antes de empezar
La terminación TLS consume muy pocos recursos: un VPS de 1 vCPU y 512 MB a 1 GB de RAM basta de sobra para servir el HTTPS de uno o varios sitios.
En cuanto a la red: su nombre de dominio debe apuntar a la IP pública del VPS (registro A/AAAA propagado). Compruebe la propagación antes de ejecutar certbot: dig +short your-domain.com debe devolver la IP de su VPS. Un challenge que falla por propagación DNS incompleta cuenta como intento y le acerca a los rate-limits de ACME.
Los puertos 80 y 443 deben estar abiertos en el firewall — el puerto 80 es indispensable para el challenge HTTP-01:
ufw allow 80/tcp
ufw allow 443/tcp
ufw reloadPara un certificado wildcard (*.your-domain.com), el challenge HTTP-01 no es suficiente: prevea un token API de su proveedor DNS (Cloudflare, OVH…) para automatizar el challenge DNS-01.
Instalar certbot y obtener un certificado Nginx
Instalar certbot y el plugin Nginx
En Debian/Ubuntu:
apt update apt install -y certbot python3-certbot-nginxEl plugin
python3-certbot-nginxlee su configuración Nginx, inyecta la configuración TLS y reemplaza la directivalisten 80por un bloque443 ssl. Si prefiere gestionar Nginx manualmente, use--standaloneo--webrooten lugar de--nginx.Apuntar el dominio y abrir los puertos
Cree un registro A
your-domain.com(y AAAA si usa IPv6) hacia la IP del VPS y luego autorice el tráfico web:ufw allow 80,443/tcpConfirme la resolución con
dig +short your-domain.comantes de seguir adelante — un challenge siempre falla si el DNS todavía no apunta.Emitir el certificado con el plugin Nginx
El método más directo — certbot modifica su vhost Nginx y gestiona la renovación:
certbot --nginx -d your-domain.com -d www.your-domain.comCertbot pide una dirección de correo (alertas de vencimiento) y le invita a aceptar los términos de servicio. Emite el certificado, modifica
/etc/nginx/sites-available/<vhost>, añade una redirección HTTP→HTTPS y recarga Nginx sin corte de servicio.Los archivos se depositan en
/etc/letsencrypt/live/your-domain.com/:fullchain.pem(certificado + cadena) yprivkey.pem(clave privada).Verificar la renovación automática
Certbot instala un timer de systemd que intenta la renovación dos veces al día:
systemctl status certbot.timerPara simular una renovación sin emitir un nuevo certificado:
certbot renew --dry-runSi la simulación tiene éxito, su cadena de renovación está operativa. Asegúrese de que Nginx se recarga tras una renovación efectiva — certbot añade automáticamente un hook
--deploy-hook "systemctl reload nginx"en/etc/letsencrypt/renewal/<domain>.confal usar el plugin Nginx.
Certificado wildcard con Cloudflare DNS challenge
Un certificado *.your-domain.com cubre todos sus subdominios con un solo archivo. Es obligatorio si crea subdominios de forma dinámica o si no quiere emitir un certificado por subdominio.
El challenge HTTP-01 no puede validar un wildcard — necesita el challenge DNS-01, que requiere que certbot cree temporalmente un registro TXT _acme-challenge.your-domain.com.
Con Cloudflare (token de API):
apt install -y python3-certbot-dns-cloudflareCree un archivo de credenciales de Cloudflare (permisos: 600):
mkdir -p /etc/letsencrypt/cloudflare
chmod 700 /etc/letsencrypt/cloudflare
cat > /etc/letsencrypt/cloudflare/credentials.ini << 'EOF'
dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN
EOF
chmod 600 /etc/letsencrypt/cloudflare/credentials.iniEmita el certificado wildcard:
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini \
-d your-domain.com \
-d '*.your-domain.com'Certbot crea el registro TXT, espera la propagación (10 segundos por defecto — aumentable con --dns-cloudflare-propagation-seconds 30), obtiene el certificado y luego elimina el registro TXT.
Alternativa con acme.sh:
acme.sh --issue --dns dns_cf -d your-domain.com -d '*.your-domain.com'acme.sh utiliza la variable de entorno CF_Token o CF_Key + CF_Email según su método de autenticación de Cloudflare.
Hardening TLS en Nginx: TLS 1.2/1.3, HSTS y cipher suites
El plugin certbot configura un HTTPS funcional, pero la configuración por defecto no es óptima para una puntuación A+ en SSL Labs. Estas son las directivas que hay que añadir en su bloque server (o en un archivo incluido como /etc/nginx/snippets/ssl-params.conf):
# Protocolos: TLS 1.2 mínimo, TLS 1.3 recomendado
ssl_protocols TLSv1.2 TLSv1.3;
# Suites de cifrado modernas (Mozilla Intermediate)
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# Sesión TLS
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
# HSTS (6 meses, subdominios incluidos, preload)
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always;
# Cabeceras de seguridad adicionales
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;Redirija todo el puerto 80 hacia el 443 en un bloque server separado:
server {
listen 80;
server_name your-domain.com www.your-domain.com;
return 301 https://$host$request_uri;
}Recargue sin corte de servicio: nginx -s reload. Luego pruebe en SSL Labs — debería obtener A+.
Supervisar la expiración de los certificados
La renovación automática puede fallar en silencio: timer de systemd desactivado, puerto 80 bloqueado tras un cambio en el firewall, token de API de Cloudflare revocado… Un certificado caducado en producción interrumpe el servicio para todos sus visitantes.
Comprobación manual:
echo | openssl s_client -connect your-domain.com:443 -servername your-domain.com 2>/dev/null \
| openssl x509 -noout -datesScript de alerta por correo (para colocar en cron o systemd):
#!/bin/bash
# Alerta si el certificado expira en menos de 14 días
cert_file="/etc/letsencrypt/live/your-domain.com/fullchain.pem"
if ! openssl x509 -checkend 1209600 -noout -in "$cert_file" 2>/dev/null; then
echo "ALERTA: certificado your-domain.com expira en menos de 14 días" \
| mail -s "[SSL] Expiración inminente" [email protected]
fi-checkend 1209600 corresponde a 14 días en segundos (14 × 86 400).
Con Uptime Kuma: añada un monitor de tipo «Certificate Expiry» que alerte a D-14 — sin scripts que mantener.
Solución de problemas: rate-limits, puerto 80 bloqueado, propagación DNS
Rate-limits ACME: «too many certificates already issued»
Let's Encrypt limita a 5 certificados por dominio registrable y por semana. Si alcanza este límite durante las pruebas, cambie al entorno de staging:
certbot --nginx --staging -d your-domain.comEl staging emite certificados no reconocidos por los navegadores, pero sin límite de tasa. Una vez validada su configuración, elimine el certificado de staging y vuelva a emitir en producción.
Puerto 80 bloqueado
El challenge HTTP-01 exige que Let's Encrypt acceda a http://your-domain.com/.well-known/acme-challenge/. Compruebe:
curl -I http://your-domain.com/.well-known/acme-challenge/test
# esperado: 404 (la ruta existe, el archivo todavía no)
ufw status | grep 80Si el puerto 80 está bloqueado por un firewall externo (proveedor cloud, Cloudflare en modo proxy), ábralo temporalmente o cambie al challenge DNS-01.
Proxy de Cloudflare activo
Si su dominio está detrás de Cloudflare (icono de nube naranja), asegúrese de que el modo SSL de Cloudflare es Full (Strict) — en modo Flexible, Cloudflare se comunica con su servidor vía HTTP y nginx al redirigir HTTP→HTTPS produce un bucle infinito (HTTP 525/526).
Propagación DNS insuficiente
Para el challenge DNS-01, espere al menos 60 segundos tras crear el registro TXT antes de relanzar certbot. Compruebe:
dig TXT _acme-challenge.your-domain.com +shortSi el valor no aparece todavía, espere antes de volver a intentarlo.
Comparativa: certbot vs acme.sh vs Caddy HTTPS automático
Desplace la tabla
| Criterio | certbot | acme.sh | Caddy (HTTPS auto) |
|---|---|---|---|
| Instalación | `apt install certbot` | Script bash, sin dependencias del sistema | Binario único, incluido |
| Integración Nginx/Apache | Plugin nativo (`--nginx`, `--apache`) | Hooks `--install-cert` manuales | Gestiona su propio reverse proxy |
| Wildcard DNS-01 | Mediante plugin DNS (`python3-certbot-dns-*`) | Nativo, muchos proveedores DNS integrados | Mediante módulo `tls.dns.*` (Caddy v2) |
| Renovación | Timer de systemd (automático) | Cron job generado en la instalación | Nativo, sin configuración |
| Curva de aprendizaje | Baja — comandos intuitivos | Media — muchas opciones | Muy baja — Caddyfile minimalista |
| Caso de uso ideal | Servidor Nginx/Apache dedicado existente | Entornos complejos, múltiples proveedores DNS | Proyectos nuevos, reverse proxy integrado |
Probar antes de la producción
Antes de pasar a producción, pruebe contra el entorno de staging de Let's Encrypt (--staging en Certbot, --server letsencrypt_test en acme.sh): el límite de producción es de 5 emisiones por dominio y por semana, y un script mal ajustado puede hacerle chocar con él muy rápido.
Añada también una supervisión del vencimiento de los certificados: una sonda que ejecute openssl x509 -checkend 604800 (7 días) y dispare una alerta si la renovación automática ha fallado en silencio — un cron roto es la causa número uno de un HTTPS caducado en plena noche.
Consulte la página de releases de certbot en GitHub para verificar la versión disponible antes de escribir un tutorial o un script de instalación.