Esta guía te lleva paso a paso por la instalación de un certificado SSL en Apache HTTP Server usando mod_ssl — desde subir los archivos hasta configuraciones TLS endurecidas y solución de errores comunes. Funciona con certificados de GetHTTPS, Certbot o cualquier CA.
Si aún no tienes un certificado, obtén uno gratis en 5 minutos.
Requisitos previos
- Apache 2.4+ instalado y sirviendo tu sitio en HTTP (puerto 80)
- Acceso root/sudo al servidor
- Archivos del certificado — Apache necesita tres archivos separados:
cert.pem— tu certificado (solo la entidad final)privkey.pem— tu clave privadachain.pem— cadena de certificados CA intermedia
¿Por qué tres archivos? A diferencia de Nginx (que usa un solo
fullchain.pem), Apache tradicionalmente espera el certificado y la cadena como archivos separados. Apache 2.4.8+ puede usarfullchain.pemenSSLCertificateFiley omitirSSLCertificateChainFile. Detalles de formatos →
Paso 1: Subir los archivos del certificado
# Crear un directorio seguro
sudo mkdir -p /etc/ssl/gethttps
# Copiar archivos al servidor
sudo cp cert.pem chain.pem privkey.pem /etc/ssl/gethttps/
# Restringir la clave privada
sudo chmod 600 /etc/ssl/gethttps/privkey.pem
sudo chmod 644 /etc/ssl/gethttps/cert.pem /etc/ssl/gethttps/chain.pem
sudo chown root:root /etc/ssl/gethttps/*
Si transfieres vía SCP:
scp cert.pem chain.pem privkey.pem user@your-server:/tmp/
# En el servidor:
sudo mv /tmp/{cert,chain,privkey}.pem /etc/ssl/gethttps/
Paso 2: Habilitar los módulos de Apache necesarios
# Debian/Ubuntu
sudo a2enmod ssl # Módulo SSL principal
sudo a2enmod headers # Para HSTS y cabeceras de seguridad
sudo a2enmod rewrite # Para redirección HTTP→HTTPS (si usas .htaccess)
sudo systemctl restart apache2
# CentOS/RHEL/Amazon Linux
sudo yum install mod_ssl # Generalmente instala y habilita en un paso
sudo systemctl restart httpd
Verifica que mod_ssl está cargado:
apachectl -M | grep ssl
# Esperado: ssl_module (shared)
Paso 3: Configurar el VirtualHost HTTPS
Crea o edita la configuración SSL de tu sitio. En Debian/Ubuntu, generalmente es /etc/apache2/sites-available/yourdomain-ssl.conf:
# ─── Servidor HTTPS ────────────────────────────
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
# Archivos del certificado
SSLEngine on
SSLCertificateFile /etc/ssl/gethttps/cert.pem
SSLCertificateKeyFile /etc/ssl/gethttps/privkey.pem
SSLCertificateChainFile /etc/ssl/gethttps/chain.pem
# Protocolo TLS — solo 1.2 y 1.3
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder off
SSLCompression off
# OCSP stapling (verificación de cert más rápida para clientes)
SSLUseStapling on
SSLStaplingCache "shmcb:/var/run/apache2/ssl_stapling(128000)"
# Cabeceras de seguridad
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
# Tu sitio
DocumentRoot /var/www/html
<Directory /var/www/html>
AllowOverride All
</Directory>
</VirtualHost>
# ─── Redirección HTTP ───────────────────────────
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://example.com/
</VirtualHost>
Qué hace cada directiva:
| Directiva | Propósito |
|---|---|
SSLEngine on | Habilita SSL/TLS para este VirtualHost |
SSLCertificateFile | Ruta a tu certificado |
SSLCertificateKeyFile | Ruta a tu clave privada |
SSLCertificateChainFile | Ruta al certificado CA intermedio |
SSLProtocol | Solo TLS 1.2/1.3 — versiones anteriores obsoletas |
SSLHonorCipherOrder off | Deja que el cliente elija la mejor suite de cifrado |
SSLCompression off | Previene el ataque CRIME |
SSLUseStapling | El servidor pre-obtiene la respuesta OCSP |
Strict-Transport-Security | HSTS — los navegadores recuerdan usar siempre HTTPS |
Redirect permanent | Redirección 301 de HTTP → HTTPS |
Paso 4: Habilitar el sitio y probar
# Debian/Ubuntu
sudo a2ensite yourdomain-ssl.conf
sudo apachectl configtest # Debe decir "Syntax OK"
sudo systemctl reload apache2
# CentOS/RHEL
sudo apachectl configtest
sudo systemctl reload httpd
Si configtest reporta un error, te indica la línea exacta — corrígela antes de recargar.
Paso 5: Verificar
Navegador: Visita https://tudominio.com. Haz clic en el candado → verifica «Let’s Encrypt» como emisor.
Línea de comandos:
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
En línea: Usa SSL Labs Server Test para una calificación completa. Objetivo: A o A+.
Apache vs Nginx: diferencias en archivos de certificado
| Apache | Nginx | |
|---|---|---|
| Certificado | SSLCertificateFile cert.pem | ssl_certificate fullchain.pem |
| Clave privada | SSLCertificateKeyFile privkey.pem | ssl_certificate_key privkey.pem |
| Cadena | SSLCertificateChainFile chain.pem | Incluida en fullchain.pem |
| Archivos necesarios | 3 archivos separados | 2 archivos (cadena combinada) |
Apache 2.4.8+ también puede usar el fullchain.pem combinado en SSLCertificateFile — entonces puedes omitir SSLCertificateChainFile.
Renovación
Cuando tu certificado se acerque al vencimiento (verifica aquí):
# Reemplazar archivos
sudo cp new-cert.pem /etc/ssl/gethttps/cert.pem
sudo cp new-chain.pem /etc/ssl/gethttps/chain.pem
sudo cp new-privkey.pem /etc/ssl/gethttps/privkey.pem
# Recargar (no reiniciar) — cero tiempo de inactividad
sudo systemctl reload apache2 # Debian/Ubuntu
sudo systemctl reload httpd # CentOS/RHEL
Guía completa de renovación → | Automatizar con Certbot →
Solución de problemas
”AH02572: Failed to configure certificate” / clave no coincide
El certificado y la clave privada no se corresponden. Verifica que coinciden:
# Estos dos hashes deben ser idénticos
openssl x509 -noout -modulus -in /etc/ssl/gethttps/cert.pem | md5sum
openssl rsa -noout -modulus -in /etc/ssl/gethttps/privkey.pem | md5sum
Si difieren, estás usando archivos de diferentes sesiones de GetHTTPS. Vuelve a descargar ambos de la misma sesión.
”AH01909: server certificate does NOT include an ID”
Tu ServerName no coincide con ningún dominio en el campo SAN del certificado:
openssl x509 -noout -text -in /etc/ssl/gethttps/cert.pem | grep -A1 "Subject Alternative Name"
Asegúrate de que tu ServerName de Apache coincida exactamente con uno de los nombres DNS listados.
El navegador muestra «certificado no confiable»
Falta la cadena intermedia. Verifica que SSLCertificateChainFile apunte a chain.pem (el intermedio), no a cert.pem. Prueba la cadena:
echo | openssl s_client -connect yourdomain.com:443 2>/dev/null | grep "Verify return code"
# "Verify return code: 0 (ok)" = bien
# "Verify return code: 21 (unable to verify)" = cadena incompleta
“AH00526: Syntax error on line X”
Error de sintaxis en la configuración de Apache. Causas comunes:
- Falta la etiqueta de cierre
</VirtualHost> SSLCertificateChainFilemal escrito- Faltan comillas alrededor de valores de cabecera
- La ruta del archivo no existe
HTTPS funciona pero muestra «No seguro»
Contenido mixto — tu página carga recursos por HTTP. Abre DevTools → Console → corrige las URLs http://.
Preguntas frecuentes
¿Puedo usar fullchain.pem en lugar de cert + chain separados?
Sí, en Apache 2.4.8+. Usa SSLCertificateFile /ruta/a/fullchain.pem y elimina completamente la directiva SSLCertificateChainFile. En versiones anteriores de Apache, necesitas archivos separados.
¿Cómo verifico qué versión de Apache tengo?
apachectl -v
# o
httpd -v
¿Necesito reiniciar Apache o solo recargar?
Recargar (systemctl reload) es suficiente y no causa tiempo de inactividad. Reiniciar (systemctl restart) interrumpe todas las conexiones activas. Siempre recarga para cambios de certificado.
¿Puede Apache servir diferentes certificados para diferentes dominios?
Sí. Crea bloques <VirtualHost *:443> separados con diferentes ServerName y directivas SSLCertificate*. Apache usa SNI (Server Name Indication) para seleccionar el certificado correcto.
¿Apache soporta certificados ECDSA?
Sí. Apache 2.4+ maneja ECDSA de la misma forma que RSA — mismas directivas, mismo formato de archivo. GetHTTPS genera ECDSA P-256 por defecto.
¿Dónde están los logs de error SSL de Apache?
# Debian/Ubuntu
tail -f /var/log/apache2/error.log
# CentOS/RHEL
tail -f /var/log/httpd/ssl_error_log