--- title: "Cómo instalar un certificado SSL propio en Nginx y Apache" description: "Genera la clave privada y el CSR con OpenSSL, instala un certificado comercial o autofirmado con su cadena en Nginx o Apache en Ubuntu 24.04 y verifica HTTPS." category: "Servidores Web" language: es source: https://cubepath.com/es/docs/servidores-web/configuracion-manual-de-ssl-tls-con-certificados-propios updated: 2026-09-25 --- # Cómo instalar un certificado SSL propio en Nginx y Apache Let's Encrypt cubre la mayoría de sitios web, pero hay casos en los que necesitas instalar un certificado emitido por otra autoridad: certificados OV o EV comprados a una CA comercial, certificados de una CA interna de tu empresa o certificados autofirmados para entornos de pruebas. En este tutorial generarás una clave privada y una solicitud de firma (CSR) con OpenSSL, montarás la cadena completa del certificado, comprobarás que todo encaja y lo configurarás en Nginx o Apache en Ubuntu 24.04. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, y un usuario no root con privilegios `sudo`. - Nginx o Apache instalado y sirviendo tu sitio por HTTP. - Un dominio con su registro DNS A apuntando al servidor. En la guía se usa `your_domain` como marcador: sustitúyelo por tu dominio real. - El puerto 443 abierto en el firewall: `sudo ufw allow 'Nginx Full'` o `sudo ufw allow 'Apache Full'`. OpenSSL viene instalado en Ubuntu 24.04. Compruébalo: ```bash openssl version ``` ```output OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024) ``` ## Paso 1: Generar la clave privada y el CSR La CA nunca ve tu clave privada: tú la generas en el servidor y le envías un CSR, que contiene la clave pública y los nombres del certificado. Crea un directorio de trabajo para los archivos del certificado y sitúate en él: ```bash sudo mkdir -p /etc/ssl/your_domain cd /etc/ssl/your_domain ``` Genera una clave RSA de 2048 bits y el CSR en un solo comando. Los navegadores solo tienen en cuenta los nombres de la extensión Subject Alternative Name (SAN), así que incluye ahí todos los dominios que debe cubrir el certificado: ```bash sudo openssl req -new -newkey rsa:2048 -nodes \ -keyout your_domain.key -out your_domain.csr \ -subj "/C=ES/O=Your Company/CN=your_domain" \ -addext "subjectAltName=DNS:your_domain,DNS:www.your_domain" ``` Qué hace cada opción: - `-newkey rsa:2048`: crea una clave RSA nueva de 2048 bits, el tamaño que aceptan todas las CA. Puedes usar `rsa:3072` si tu política lo exige. - `-nodes`: guarda la clave sin contraseña. Es necesario para que Nginx o Apache arranquen sin intervención manual. - `-subj`: rellena el sujeto sin preguntas interactivas. Para certificados DV basta con `CN`; para OV o EV, la CA validará los datos de la organización. - `-addext`: añade la extensión SAN al CSR. Para un certificado wildcard, usa `DNS:your_domain,DNS:*.your_domain` en el SAN. Si prefieres una clave ECDSA, más pequeña y rápida, sustituye `-newkey rsa:2048` por `-newkey ec -pkeyopt ec_paramgen_curve:prime256v1`, siempre que tu CA la admita. Protege la clave privada: ```bash sudo chmod 600 your_domain.key ``` Revisa el contenido del CSR antes de enviarlo: ```bash sudo openssl req -in your_domain.csr -noout -text -verify ``` ```output Certificate request self-signature verify OK Certificate Request: Data: Version: 1 (0x0) Subject: C = ES, O = Your Company, CN = your_domain ... Requested Extensions: X509v3 Subject Alternative Name: DNS:your_domain, DNS:www.your_domain ``` Muestra el CSR para copiarlo en el formulario de la CA: ```bash sudo cat your_domain.csr ``` Copia el bloque completo, incluidas las líneas `-----BEGIN CERTIFICATE REQUEST-----` y `-----END CERTIFICATE REQUEST-----`. > Advertencia: `your_domain.key` no debe salir nunca del servidor. No la envíes a la CA, no la subas a un repositorio y no la guardes en copias de seguridad sin cifrar. ## Paso 2: Montar la cadena completa del certificado Cuando la CA valide tu solicitud te entregará, como mínimo, el certificado del dominio y uno o varios certificados intermedios (a veces en un archivo llamado `ca-bundle`, `chain` o `intermediate`). El servidor debe enviar al cliente el certificado del dominio seguido de los intermedios; si falta un intermedio, muchos clientes (móviles, `curl`, APIs) rechazarán la conexión aunque el navegador de tu equipo funcione. Sube los archivos al directorio de trabajo, por ejemplo con `scp`, y compruébalos. El certificado del dominio debe mostrar tu dominio como sujeto: ```bash sudo openssl x509 -in your_domain.crt -noout -subject -issuer -dates ``` ```output subject=CN = your_domain issuer=C = US, O = Example CA, CN = Example CA Intermediate notBefore=Sep 25 00:00:00 2026 GMT notAfter=Sep 24 23:59:59 2027 GMT ``` El `issuer` del certificado del dominio debe coincidir con el sujeto del primer intermedio. Crea el archivo de cadena completa concatenando ambos en ese orden (dominio primero, intermedios después, sin la raíz): ```bash sudo sh -c 'cat your_domain.crt intermediate.crt > your_domain.fullchain.crt' ``` Verifica que la cadena conduce a una raíz de confianza del sistema: ```bash sudo openssl verify -untrusted intermediate.crt your_domain.crt ``` ```output your_domain.crt: OK ``` Si la CA es interna, añade `-CAfile ruta/a/la/raiz.crt` al comando, ya que su raíz no está en el almacén del sistema. ## Paso 3: Comprobar que la clave privada corresponde al certificado Un error frecuente es instalar un certificado emitido para otro CSR. Compara la clave pública del certificado con la de tu clave privada: ```bash sudo openssl x509 -in your_domain.crt -noout -pubkey | sha256sum sudo openssl pkey -in your_domain.key -pubout | sha256sum ``` ```output 3f9d0c1e...a41b - 3f9d0c1e...a41b - ``` Los dos hashes deben ser idénticos. Si difieren, el certificado no corresponde a esta clave y el servidor web se negará a arrancar. ## Alternativa: crear un certificado autofirmado para pruebas Si solo necesitas HTTPS en un entorno de desarrollo o en un servicio interno, puedes generar la clave y un certificado autofirmado en un único paso, sin CSR ni CA: ```bash sudo openssl req -x509 -newkey rsa:2048 -nodes -days 365 \ -keyout /etc/ssl/your_domain/your_domain.key \ -out /etc/ssl/your_domain/your_domain.fullchain.crt \ -subj "/CN=your_domain" \ -addext "subjectAltName=DNS:your_domain,DNS:www.your_domain" sudo chmod 600 /etc/ssl/your_domain/your_domain.key ``` Los navegadores mostrarán una advertencia porque ninguna CA de confianza lo ha firmado. Es adecuado para pruebas, no para un sitio público. Con este certificado puedes saltarte los pasos 2 y 3 y usar las mismas rutas en la configuración del servidor. ## Paso 4: Configurar el certificado en Nginx Abre el archivo de tu sitio: ```bash sudo nano /etc/nginx/sites-available/your_domain ``` Añade un bloque para HTTPS y redirige el tráfico HTTP. Ajusta `root` a la ruta de tu sitio: ```nginx server { listen 80; listen [::]:80; server_name your_domain www.your_domain; return 301 https://$host$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; server_name your_domain www.your_domain; ssl_certificate /etc/ssl/your_domain/your_domain.fullchain.crt; ssl_certificate_key /etc/ssl/your_domain/your_domain.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; root /var/www/your_domain; index index.html; location / { try_files $uri $uri/ =404; } } ``` `ssl_certificate` debe apuntar al archivo de cadena completa, no solo al certificado del dominio. Activa el sitio si aún no lo está, valida la sintaxis y recarga: ```bash sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ```output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` Si el enlace simbólico ya existía, `ln` mostrará un error que puedes ignorar. ## Paso 4 (Apache): Configurar el certificado en Apache Si usas Apache, activa los módulos `ssl` y `rewrite`: ```bash sudo a2enmod ssl rewrite ``` Crea el VirtualHost para HTTPS: ```bash sudo nano /etc/apache2/sites-available/your_domain-ssl.conf ``` ```apache ServerName your_domain ServerAlias www.your_domain DocumentRoot /var/www/your_domain SSLEngine on SSLCertificateFile /etc/ssl/your_domain/your_domain.fullchain.crt SSLCertificateKeyFile /etc/ssl/your_domain/your_domain.key SSLProtocol -all +TLSv1.2 +TLSv1.3 ErrorLog ${APACHE_LOG_DIR}/your_domain-ssl-error.log CustomLog ${APACHE_LOG_DIR}/your_domain-ssl-access.log combined ``` Desde Apache 2.4.8, `SSLCertificateFile` admite el certificado con sus intermedios en el mismo archivo, por lo que ya no hace falta `SSLCertificateChainFile`. En el VirtualHost del puerto 80 (`/etc/apache2/sites-available/your_domain.conf`) añade la redirección: ```apache ServerName your_domain ServerAlias www.your_domain Redirect permanent / https://your_domain/ ``` Activa el sitio, valida y recarga: ```bash sudo a2ensite your_domain-ssl sudo apache2ctl configtest sudo systemctl reload apache2 ``` ```output Syntax OK ``` ## Paso 5: Verificar la instalación Comprueba desde el propio servidor que HTTPS responde con el certificado correcto: ```bash curl -I https://your_domain ``` ```output HTTP/1.1 200 OK Server: nginx/1.24.0 (Ubuntu) ``` Con un certificado autofirmado, `curl` fallará con `SSL certificate problem: self-signed certificate`; añade `-k` para ignorar la validación en pruebas. Revisa la cadena que envía el servidor. Deben aparecer el certificado del dominio (`0`) y al menos un intermedio (`1`): ```bash echo | openssl s_client -connect your_domain:443 -servername your_domain -showcerts 2>/dev/null | grep -E '^ ?[0-9] s:|Verify return code' ``` ```output 0 s:CN = your_domain 1 s:C = US, O = Example CA, CN = Example CA Intermediate Verify return code: 0 (ok) ``` Un `Verify return code` distinto de `0 (ok)`, como `21 (unable to verify the first certificate)`, indica que falta un intermedio en el archivo de cadena completa. ## Paso 6: Controlar la fecha de caducidad Los certificados comerciales no se renuevan solos: tendrás que generar un CSR nuevo (o reutilizar el actual, si tu CA lo permite), obtener el certificado y repetir los pasos 2 a 5. Consulta la fecha de caducidad del certificado instalado: ```bash sudo openssl x509 -in /etc/ssl/your_domain/your_domain.fullchain.crt -noout -enddate ``` ```output notAfter=Sep 24 23:59:59 2027 GMT ``` La opción `-checkend` devuelve un código de salida distinto de cero si el certificado caduca dentro del número de segundos indicado, lo que permite integrarla en tu sistema de monitorización. Por ejemplo, para 30 días: ```bash sudo openssl x509 -in /etc/ssl/your_domain/your_domain.fullchain.crt -noout -checkend 2592000 ``` ```output Certificate will not expire ``` Desde 2026 los certificados públicos tienen una validez máxima cada vez más corta, así que conviene tener esta comprobación automatizada. ## Solución de problemas - **Nginx no arranca con `key values mismatch`** o Apache con `AH02565: Certificate and private key ... do not match`: el certificado no corresponde a la clave. Repite la comprobación del paso 3 y usa la clave con la que generaste el CSR. - **`unable to verify the first certificate` en `curl` o en apps móviles**: falta el intermedio. Rehaz `your_domain.fullchain.crt` con el certificado del dominio seguido del intermedio que te dio la CA. - **`PEM_read_bio_X509_AUX ... no start line`**: el archivo no está en formato PEM. Si la CA te dio un `.der` o `.cer` binario, conviértelo con `openssl x509 -inform der -in cert.cer -out cert.crt`. - **El navegador indica `NET::ERR_CERT_COMMON_NAME_INVALID`**: el dominio al que accedes no está en el SAN del certificado. Comprueba los nombres con `openssl x509 -in your_domain.crt -noout -ext subjectAltName`. ## Conclusión Has generado una clave privada y un CSR con OpenSSL, montado la cadena completa del certificado, verificado que la clave y el certificado coinciden y configurado HTTPS en Nginx o Apache con redirección desde HTTP. El mismo procedimiento sirve para certificados de una CA comercial, de una CA interna o autofirmados. Como siguientes pasos puedes: - Añadir un recordatorio o una comprobación automática con `openssl x509 -checkend` antes de cada caducidad. - Activar HTTP/2 en el servidor web ahora que el sitio sirve HTTPS. - Valorar Let's Encrypt con Certbot para los dominios que no necesiten validación OV o EV, ya que la renovación es automática.