# Solución a Error “bad cipher” en Nginx detrás de Cloudflare con OpenSSL 3.x

- Link: https://binariocero.com/linux/solucion-a-error-bad-cipher-en-nginx-detras-de-cloudflare-con-openssl-3-x
- Published: 2025-08-15T09:28:52+02:00
- Author: Juan José Fernández Durán

![Loading](https://binariocero.com/wp-content/plugins/page-views-count/ajax-loader-
2x.gif)

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:

    ```wp-block-code
    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”

 1. **Lista de cifrados (`ssl_ciphers`) mal definida**
 2.  * 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.
 3. **Protocolos limitados**
 4.  * Habilitar solo TLS 1.3 y bloquear TLS 1.2, cuando Cloudflare intenta 1.2 hacia
       el origen.
 5. **Políticas de seguridad elevadas (SECLEVEL)**
 6.  * En OpenSSL 3.x, un `SECLEVEL` alto puede bloquear cifrados válidos para Cloudflare.
 7. **Conflictos de configuración**
 8.  * Otro bloque `server {}` que atiende el mismo `server_name` con parámetros SSL
       distintos.

## 2. Comprobar versión y compilación de OpenSSL

Antes de modificar Nginx, conviene confirmar qué versión de OpenSSL usa:

    ```wp-block-code
    nginx -V 2>&1 | grep -o 'OpenSSL[^ ]* [^ ]*'
    openssl version -a
    ```

Ejemplo de salida con OpenSSL 3.x:

    ```wp-block-code
    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.

    ```wp-block-code
    # 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 en `ssl_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:

    ```wp-block-code
    nginx -t && systemctl reload nginx
    ```

Pruebas desde el origen:

    ```wp-block-code
    # 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:

    ```wp-block-code
    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:

 1. Habilitar TLS 1.2 y TLS 1.3.
 2. Definir listas de cifrados seguras y compatibles.
 3. Mantener configuraciones separadas para TLS 1.2 y TLS 1.3.
 4. 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.
