Cuando se utiliza Cloudflare como proxy inverso delante de un servidor Nginx con OpenSSL 3.x, es posible encontrarse en los logs con un error como:
SSL_do_handshake() failed (SSL: error:0A0000BA:SSL routines::bad cipher) while SSL handshaking, client: 172.71.xxx.xxx, server: 0.0.0.0:443
Este mensaje indica que el cliente (Cloudflare) y el servidor (Nginx) no han encontrado ningún cifrado común para negociar la conexión TLS. Aunque el certificado esté correcto, el fallo ocurre antes de intercambiar datos, durante el “handshake”.
1. Causas comunes del “bad cipher”
- Lista de cifrados (
ssl_ciphers
) mal definida- Incluir solo suites TLS 1.3 dentro de
ssl_ciphers
(estas no aplican a TLS 1.2). - Lista demasiado restrictiva que no coincide con las soportadas por Cloudflare.
- Incluir solo suites TLS 1.3 dentro de
- Protocolos limitados
- Habilitar solo TLS 1.3 y bloquear TLS 1.2, cuando Cloudflare intenta 1.2 hacia el origen.
- Políticas de seguridad elevadas (SECLEVEL)
- En OpenSSL 3.x, un
SECLEVEL
alto puede bloquear cifrados válidos para Cloudflare.
- En OpenSSL 3.x, un
- Conflictos de configuración
- Otro bloque
server {}
que atiende el mismoserver_name
con parámetros SSL distintos.
- Otro bloque
2. Comprobar versión y compilación de OpenSSL
Antes de modificar Nginx, conviene confirmar qué versión de OpenSSL usa:
nginx -V 2>&1 | grep -o 'OpenSSL[^ ]* [^ ]*'
openssl version -a
Ejemplo de salida con OpenSSL 3.x:
OpenSSL 3.5.1 1 Jul 2025
built on: ...
platform: debian-amd64
OPENSSLDIR: "/usr/lib/ssl"
3. Configuración recomendada en Nginx
Para asegurar compatibilidad con Cloudflare y mantener buenas prácticas de seguridad, se recomienda habilitar TLS 1.2 y TLS 1.3, definiendo las suites de forma correcta.
Este bloque se puede incluir en http {}
para que afecte a todos los server {}
, o de forma individual en cada server
de 443.
# Protocolos compatibles con Cloudflare
ssl_protocols TLSv1.2 TLSv1.3;
# TLS 1.2: suites modernas y comunes con Cloudflare
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_prefer_server_ciphers on; # Controla el orden de preferencia (ver sección 5)
ssl_ecdh_curve X25519:P-256;
# TLS 1.3: se definen aparte en OpenSSL 3.x
ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
# Opcional (rendimiento y OCSP)
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 valid=300s;
resolver_timeout 5s;
Nota: No mezclar suites TLS 1.3 en
ssl_ciphers
; se definen solo enssl_conf_command
.
4. Ajustes en Cloudflare
En el panel de Cloudflare para el dominio (example.com
en este ejemplo):
- SSL/TLS mode:
Full (strict)
- Minimum TLS version:
1.2
- Certificado en el origen válido (Let’s Encrypt o Cloudflare Origin CA).
- Si usas Cloudflare Origin CA, añadir:
ssl_trusted_certificate /etc/ssl/cloudflare_origin_ca.pem;
5. Sobre ssl_prefer_server_ciphers
- on → El servidor impone el orden de
ssl_ciphers
en TLS 1.2. - off → El cliente (Cloudflare) elige su preferida de las permitidas.
- TLS 1.3: No se ve afectado por esta directiva.
En entornos donde se prioriza compatibilidad, off
puede evitar conflictos. Si se quiere controlar la elección por motivos de rendimiento o compliance, se deja on
.
6. Verificación
Tras aplicar cambios y recargar Nginx:
nginx -t && systemctl reload nginx
Pruebas desde el origen:
# Forzar TLS 1.3
openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null | grep -E 'Protocol|Cipher'
# Forzar TLS 1.2
openssl s_client -connect example.com:443 -servername example.com -tls1_2 </dev/null | grep -E 'Protocol|Cipher'
Ejemplo de resultado esperado:
Protocol : TLSv1.3
Cipher : TLS_AES_128_GCM_SHA256
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES128-GCM-SHA256
7. Conclusión
El error “bad cipher” con Cloudflare y Nginx sobre OpenSSL 3.x se debe normalmente a una falta de intersección de suites de cifrado entre ambos.
La solución pasa por:
- Habilitar TLS 1.2 y TLS 1.3.
- Definir listas de cifrados seguras y compatibles.
- Mantener configuraciones separadas para TLS 1.2 y TLS 1.3.
- Ajustar Cloudflare a
Full (strict)
y TLS mínimo 1.2.
Con esta configuración, el handshake TLS se establecerá correctamente y desaparecerá el “bad cipher” en los logs.
Compartir: