Esta guía te lleva paso a paso por la instalación de un certificado SSL en Nginx: desde la subida de archivos hasta la optimización de la configuración TLS y la solución de errores comunes. Funciona con certificados de GetHTTPS, Certbot o cualquier otra fuente.
Si aún no tienes un certificado, obtén uno gratis en 5 minutos.
Requisitos previos
- Nginx instalado y sirviendo tu sitio en HTTP (puerto 80)
- Acceso root/sudo al servidor
- Archivos de certificado — necesitas dos:
fullchain.pem— tu certificado + cadena intermedia (combinados)privkey.pem— tu clave privada
¿Qué archivo es cuál? GetHTTPS te da cuatro archivos. Nginx necesita
fullchain.pem(nocert.pem) yprivkey.pem. Usar solocert.pemcausa errores de «cadena incompleta» porque los navegadores no pueden verificar la ruta de confianza. Formatos de certificados explicados →
Paso 1: Subir los archivos de certificado al servidor
Copia los archivos a un directorio seguro:
# Create directory
sudo mkdir -p /etc/ssl/gethttps
# Copy files (from your local machine to the server)
sudo cp fullchain.pem /etc/ssl/gethttps/
sudo cp privkey.pem /etc/ssl/gethttps/
# Restrict private key permissions — only root should read it
sudo chmod 600 /etc/ssl/gethttps/privkey.pem
sudo chmod 644 /etc/ssl/gethttps/fullchain.pem
sudo chown root:root /etc/ssl/gethttps/*
Si transfieres vía SCP:
scp fullchain.pem privkey.pem user@your-server:/tmp/
# Then on the server:
sudo mv /tmp/fullchain.pem /tmp/privkey.pem /etc/ssl/gethttps/
Paso 2: Configurar el bloque server HTTPS
Edita la configuración de Nginx de tu sitio. El archivo normalmente se encuentra en:
/etc/nginx/sites-available/yourdomain.conf(Debian/Ubuntu)/etc/nginx/conf.d/yourdomain.conf(CentOS/RHEL)
Configuración completa recomendada:
# HTTPS server block
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# ─── Certificate files ───────────────────────
ssl_certificate /etc/ssl/gethttps/fullchain.pem;
ssl_certificate_key /etc/ssl/gethttps/privkey.pem;
# ─── TLS protocol and ciphers ────────────────
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
# ─── Session caching (performance) ───────────
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# ─── OCSP stapling (faster verification) ─────
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# ─── Security headers ────────────────────────
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
# ─── Your site ───────────────────────────────
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
# HTTP redirect block
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
Qué hace cada sección:
| Directiva | Propósito |
|---|---|
listen 443 ssl http2 | HTTPS en el puerto 443 con HTTP/2 habilitado |
ssl_certificate | Ruta a tu certificado + cadena |
ssl_certificate_key | Ruta a tu clave privada |
ssl_protocols | Solo TLS 1.2 y 1.3 (1.0/1.1 obsoletos desde 2020) |
ssl_prefer_server_ciphers off | Permite al cliente elegir el mejor cifrado (práctica recomendada moderna) |
ssl_session_cache | Almacena sesiones TLS en caché — evita re-negociación para visitantes recurrentes |
ssl_session_tickets off | Los tickets pueden debilitar el secreto perfecto hacia adelante |
ssl_stapling | El servidor obtiene la respuesta OCSP — más rápido para los clientes |
Strict-Transport-Security | HSTS — los navegadores recuerdan usar siempre HTTPS |
return 301 | Redirección permanente de HTTP a HTTPS |
Paso 3: Probar la configuración
Siempre prueba antes de recargar:
sudo nginx -t
Resultado esperado:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Si la prueba falla, Nginx te indica el número de línea exacto con el error. Errores comunes:
- Error tipográfico en la ruta del archivo (
ssl_certifcateen lugar dessl_certificate) - El archivo no existe en la ruta especificada
- Puntos y comas faltantes
Paso 4: Recargar Nginx
sudo systemctl reload nginx
Usa reload, no restart. Reload aplica la nueva configuración a las nuevas conexiones sin cortar las existentes: cero tiempo de inactividad.
Paso 5: Verificar el certificado
Verificación en el navegador
Visita https://yourdomain.com. Haz clic en el icono de candado para confirmar:
- Emitido por: «Let’s Encrypt» (o tu CA)
- Válido: fechas de inicio y fin
- Dominio: coincide con tu URL
Verificación por línea de comandos
# Full certificate details
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName
# Check the full chain is present
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
| grep -E 'Verify return code|depth='
Esperado: Verify return code: 0 (ok) y múltiples niveles de profundidad (tu certificado + intermedio).
Verificación en línea
Usa SSL Labs Server Test para un informe completo. Verifica:
- Validez de la cadena de certificados
- Soporte de protocolos (debe mostrar solo TLS 1.2 + 1.3)
- Fortaleza de las suites de cifrado
- Vulnerabilidades conocidas (BEAST, POODLE, Heartbleed)
- Configuración HSTS
Objetivo: Grado A o A+.
Cómo renovar
Cuando tu certificado se acerque al vencimiento (día 60 de 90 para Let’s Encrypt):
- Obtén un nuevo certificado desde GetHTTPS (o tu herramienta de renovación)
- Reemplaza los archivos:
sudo cp new-fullchain.pem /etc/ssl/gethttps/fullchain.pem sudo cp new-privkey.pem /etc/ssl/gethttps/privkey.pem - Recarga Nginx:
sudo systemctl reload nginx
No se necesita reinicio. Nginx lee los nuevos archivos al recargar. Guía completa de renovación →
¿Quieres renovación automática? Instala Certbot para renovación sin intervención:
sudo certbot --nginx -d example.com. Se encarga de todo, incluyendo la configuración de Nginx.
Solución de problemas
«SSL: error:0B080074:x509 certificate routines»
Causa: Estás usando cert.pem en lugar de fullchain.pem.
Solución: Cambia ssl_certificate para que apunte a fullchain.pem, que incluye tanto tu certificado como la cadena de confianza intermedia:
ssl_certificate /etc/ssl/gethttps/fullchain.pem; # NOT cert.pem
«cannot load certificate key»
Causa: Los permisos del archivo de clave privada son incorrectos, el archivo está corrupto o la clave no coincide con el certificado.
Solución:
# Check permissions (should be 600)
ls -la /etc/ssl/gethttps/privkey.pem
# Verify the key is valid
sudo openssl ec -in /etc/ssl/gethttps/privkey.pem -check # For ECDSA
sudo openssl rsa -in /etc/ssl/gethttps/privkey.pem -check # For RSA
# Verify key matches certificate
sudo openssl x509 -noout -modulus -in /etc/ssl/gethttps/fullchain.pem | md5sum
sudo openssl rsa -noout -modulus -in /etc/ssl/gethttps/privkey.pem | md5sum
# Both hashes MUST match
«nginx: [emerg] bind() to 0.0.0.0:443 failed»
Causa: El puerto 443 ya está en uso por otro proceso.
Solución:
# Find what's using port 443
sudo ss -tlnp | grep 443
# Usually: another Nginx instance or Apache
sudo systemctl stop apache2 # If Apache is running
sudo systemctl reload nginx
HTTPS funciona pero el navegador muestra «Not Secure»
Causa: Contenido mixto — tu página carga imágenes, scripts o CSS a través de http://.
Solución: Abre DevTools (F12) → Consola → busca advertencias de «Mixed Content». Actualiza todas las URLs de recursos para usar https:// o rutas relativas.
«ERR_SSL_VERSION_OR_CIPHER_MISMATCH»
Causa: El cliente no soporta las versiones TLS o los cifrados que ofrece tu servidor.
Solución: Asegúrate de que tu configuración incluya TLS 1.2 (no solo 1.3):
ssl_protocols TLSv1.2 TLSv1.3; # NOT just TLSv1.3
El certificado aparece como «Untrusted» a pesar de los archivos correctos
Causa: La cadena de certificados está incompleta — fullchain.pem no tiene el certificado intermedio, o el archivo está en el orden equivocado.
Solución: Reconstruye fullchain.pem concatenando tu certificado + el intermedio:
cat cert.pem chain.pem > fullchain.pem
El orden importa: tu certificado primero, luego el intermedio.
Preguntas frecuentes
¿Necesito archivos de configuración separados para HTTP y HTTPS?
No. Pon ambos bloques server (puerto 80 y 443) en el mismo archivo. El bloque HTTP solo existe para redirigir a HTTPS.
¿Debo usar ssl on;?
No. ssl on; está obsoleto. Usa listen 443 ssl; en su lugar: el parámetro ssl en la directiva listen es el enfoque moderno.
¿Cuál es la diferencia entre reload y restart?
reload aplica la nueva configuración a las nuevas conexiones sin cortar las existentes. restart detiene e inicia el proceso, cortando brevemente todas las conexiones. Siempre usa reload para cambios de certificados.
¿Puedo servir múltiples dominios con diferentes certificados?
Sí. Usa bloques server separados con diferentes directivas server_name, ssl_certificate y ssl_certificate_key. Nginx usa SNI (Server Name Indication) para servir el certificado correcto según el nombre de host solicitado.
¿Nginx soporta certificados ECDSA/ECC?
Sí. Nginx maneja los certificados ECDSA exactamente igual que RSA: mismas directivas, mismo formato de archivo. GetHTTPS genera ECDSA P-256 por defecto (claves más pequeñas, negociaciones más rápidas).
¿Cómo habilito HTTP/2?
Añade http2 a la directiva listen: listen 443 ssl http2;. Esto ya está en la configuración recomendada de arriba. HTTP/2 requiere HTTPS: no puedes usarlo sobre HTTP plano.