SSL Labs (de Qualys) es el test público más usado para evaluar la configuración TLS de un servidor web. Asigna una nota de A+ a F según los protocolos, cifrados, certificado y cabeceras que ofrece tu sitio. En esta guía configurarás Nginx o Apache en Ubuntu 24.04 con un certificado de Let's Encrypt, TLS 1.2 y 1.3 con cifrados modernos y la cabecera HSTS, que es lo que separa una A de una A+, y comprobarás el resultado en SSL Labs.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con un usuario no root con privilegios sudo.
  • Nginx o Apache instalado y sirviendo tu sitio por HTTP.
  • Un dominio (your_domain en los ejemplos) con registros A (y AAAA si usas IPv6) apuntando al servidor. Si tienes www, también debe apuntar al servidor.
  • Puertos 80 y 443 abiertos. Con UFW:
sudo ufw allow 'Nginx Full'

Si usas Apache, el perfil es 'Apache Full'.

Qué necesita una A+

SSL Labs parte de una puntuación numérica y después aplica límites que bajan la nota ante problemas concretos. Para llegar a A+ necesitas:

RequisitoQué pasa si falta
Certificado válido, cadena completa y nombre correctoNota T o F
Sin SSL 3, TLS 1.0 ni TLS 1.1Limitado a B (C o F si hay SSL 3)
Secreto hacia adelante (ECDHE) en todas las suitesLimitado a B
Sin cifrados inseguros (RC4, 3DES, NULL, EXPORT)Limitado a B, C o F
Soporte de TLS 1.3Recomendado; los servidores modernos lo tienen por defecto
Cabecera HSTS con max-age de al menos 6 mesesSe queda en A

El OCSP stapling, las cabeceras como CSP o X-Frame-Options y el preload de HSTS no forman parte de la nota. Son buenas prácticas, pero no hace falta nada de eso para la A+.

Paso 1: Obtener un certificado ECDSA con Certbot

Instala Certbot con el plugin de tu servidor web:

sudo apt update
sudo apt install certbot python3-certbot-nginx

Para Apache, instala python3-certbot-apache en lugar de python3-certbot-nginx.

Desde la versión 2.0, Certbot genera por defecto claves ECDSA P-256, más pequeñas y rápidas que RSA 2048 con una seguridad equivalente o mayor. Ubuntu 24.04 trae Certbot 2.9, así que no necesitas opciones extra. Solicita el certificado solo (certonly) para que Certbot no modifique la configuración TLS del sitio; la escribirás tú en el paso 2:

sudo certbot certonly --nginx -d your_domain -d www.your_domain

Con Apache, usa --apache en lugar de --nginx.

Comprueba el tipo de clave:

sudo openssl x509 -in /etc/letsencrypt/live/your_domain/cert.pem -noout -text | grep 'Public Key Algorithm'
            Public Key Algorithm: id-ecPublicKey

El paquete de Certbot instala un temporizador de systemd que renueva los certificados automáticamente. Verifica que está activo:

systemctl list-timers certbot.timer

Paso 2: Configurar TLS y HSTS en Nginx

En Ubuntu, /etc/nginx/nginx.conf ya activa TLS 1.0 y 1.1 en su sección SSL Settings, y eso limitaría la nota a B. Edítalo:

sudo nano /etc/nginx/nginx.conf

Deja la sección SSL Settings del bloque http así:

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    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;
    ssl_ecdh_curve X25519:prime256v1:secp384r1;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

Estos cifrados solo usan ECDHE (secreto hacia adelante) con AES-GCM o ChaCha20, y ninguno aparece como débil en SSL Labs. ssl_ciphers no afecta a TLS 1.3, que usa las suites seguras de OpenSSL por defecto.

Ahora crea o sustituye la configuración del sitio:

sudo nano /etc/nginx/sites-available/your_domain
server {
    listen 80;
    listen [::]:80;
    server_name your_domain www.your_domain;

    return 301 https://your_domain$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name your_domain www.your_domain;

    ssl_certificate     /etc/letsencrypt/live/your_domain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    root /var/www/your_domain;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

Puntos importantes:

  • fullchain.pem incluye el certificado intermedio. Si usas cert.pem, la cadena queda incompleta y SSL Labs lo penaliza.
  • max-age=31536000 es un año, por encima de los 6 meses que exige la A+. always hace que la cabecera se envíe también en respuestas de error.
  • includeSubDomains obliga a usar HTTPS en todos los subdominios. Quítalo si alguno sigue sirviendo solo HTTP.
  • En Nginx, un add_header dentro de un bloque location anula todos los add_header del bloque server. Si añades cabeceras en un location, repite ahí la de HSTS.

Activa el sitio si es nuevo, comprueba la sintaxis y recarga:

sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Paso 3 (alternativa): Configurar TLS y HSTS en Apache

Si usas Apache, activa los módulos necesarios:

sudo a2enmod ssl headers

Crea un archivo global con la política TLS. Se carga después de mods-available/ssl.conf y sustituye sus valores:

sudo nano /etc/apache2/conf-available/tls-modern.conf
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLHonorCipherOrder off
SSLCipherSuite 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
SSLOpenSSLConfCmd Curves X25519:prime256v1:secp384r1
SSLSessionTickets off
sudo a2enconf tls-modern

Crea el sitio con la redirección y el VirtualHost HTTPS:

sudo nano /etc/apache2/sites-available/your_domain.conf
<VirtualHost *:80>
    ServerName your_domain
    ServerAlias www.your_domain
    Redirect permanent / https://your_domain/
</VirtualHost>

<VirtualHost *:443>
    ServerName your_domain
    ServerAlias www.your_domain
    DocumentRoot /var/www/your_domain

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/your_domain/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/your_domain/privkey.pem

    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

    ErrorLog ${APACHE_LOG_DIR}/your_domain_error.log
    CustomLog ${APACHE_LOG_DIR}/your_domain_access.log combined
</VirtualHost>

Actívalo, comprueba y recarga:

sudo a2ensite your_domain
sudo apache2ctl configtest
sudo systemctl reload apache2
Syntax OK

Paso 4: Comprobar la configuración antes del test

SSL Labs tarda un par de minutos, así que conviene verificar lo básico antes desde el servidor.

La redirección de HTTP a HTTPS:

curl -sI http://your_domain | grep -iE '^(HTTP|location)'
HTTP/1.1 301 Moved Permanently
Location: https://your_domain/

La cabecera HSTS en HTTPS:

curl -sI https://your_domain | grep -i strict-transport-security
strict-transport-security: max-age=31536000; includeSubDomains

El protocolo y la cadena de certificados:

openssl s_client -connect your_domain:443 -servername your_domain < /dev/null 2>/dev/null | grep -E 'Verify return code|^New'
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Verify return code: 0 (ok)

Un código distinto de 0 (ok) indica un problema de cadena o de nombre que SSL Labs también detectará.

Paso 5: Ejecutar el test de SSL Labs

Abre https://www.ssllabs.com/ssltest/, escribe tu dominio y marca Do not show the results on the boards si no quieres que aparezca en el listado público. El análisis tarda entre uno y tres minutos y prueba cada dirección IP del dominio por separado (IPv4 e IPv6), así que ambas deben tener la misma configuración.

El resumen debe mostrar Overall Rating: A+. En la sección de detalles verás:

  • Protocols: TLS 1.3 y TLS 1.2 en Yes; TLS 1.1, TLS 1.0, SSL 3 y SSL 2 en No.
  • Cipher Suites: ninguna marcada como WEAK.
  • HTTP Strict Transport Security (HSTS): Yes con el max-age que configuraste.

Si repites el test tras un cambio, pulsa Clear cache en la página de resultados; si no, SSL Labs muestra el análisis anterior.

Sobre el preload de HSTS

El preload de HSTS (añadir preload a la cabecera y registrar el dominio en hstspreload.org) hace que los navegadores usen HTTPS desde la primera visita. No sube la nota y es difícil de deshacer: salir de la lista tarda meses y, mientras tanto, cualquier subdominio sin HTTPS deja de ser accesible. Actívalo solo cuando estés seguro de que todo el dominio, incluidos los subdominios, funciona por HTTPS y exige max-age de al menos un año.

Solución de problemas

Nota B con el aviso "This server supports TLS 1.0 and TLS 1.1". Otra configuración vuelve a activar esos protocolos. En Nginx suele ser include /etc/letsencrypt/options-ssl-nginx.conf; en el bloque server o un sitio default que escucha en el mismo puerto. Búscalos:

sudo grep -rnE 'ssl_protocols|SSLProtocol|options-ssl' /etc/nginx /etc/apache2

Se queda en A y SSL Labs no detecta HSTS. Comprueba con curl -sI https://your_domain que la cabecera sale en la respuesta de la raíz. En Nginx, revisa si algún location tiene su propio add_header. En Apache, confirma que el módulo headers está activo con apache2ctl -M | grep headers.

"Chain issues: Incomplete". El servidor no envía el intermedio. Usa fullchain.pem en ssl_certificate o en SSLCertificateFile.

Nota distinta en IPv4 e IPv6. El registro AAAA apunta a otra máquina o Nginx no tiene listen [::]:443. Corrige el DNS o la directiva listen.

Conclusión

Has configurado un certificado ECDSA de Let's Encrypt con renovación automática, limitado el servidor a TLS 1.2 y 1.3 con cifrados que ofrecen secreto hacia adelante, forzado HTTPS y añadido la cabecera HSTS, que es el último requisito para la A+. Como siguientes pasos, puedes añadir cabeceras de seguridad (Content-Security-Policy, X-Content-Type-Options) y revisarlas en securityheaders.com, y repetir el test de SSL Labs tras cada cambio de configuración o de certificado.