Сообщение об ошибке
code: 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE'
Также выводится как
- ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE
Код повторяет строку причины из OpenSSL: релизы Node.js со встроенным OpenSSL 3.0 или 3.1 выдают ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE (от «sslv3 alert handshake failure»), а текущие релизы с OpenSSL 3.5 — ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE. При вызове https.request() то же оповещение о сбое рукопожатия выглядит как «write EPROTO … SSL alert number 40».
Node.js tls.connect / fetch — TLS alert 40 (handshake_failure) received from the server
Что это означает
Сервер ответил на ClientHello от Node.js оповещением TLS 40 (handshake_failure) и закрыл соединение. Он не нашёл приемлемого сочетания набора шифров, группы обмена ключами и алгоритма подписи либо требует клиентский сертификат, который не был отправлен. Node.js формирует код ошибки из текста причины в OpenSSL, поэтому в коде есть «SSL/TLS».
Почему это происходит
Нет общего набора шифров
частоСтарые серверы, у которых есть только наборы шифров с 3DES, RC4 или обменом ключами RSA, сталкиваются со списком шифров Node.js по умолчанию, в котором таких наборов уже нет.
Сервер требует клиентский сертификат (mTLS)
частоПлатёжные, банковские и B2B API часто требуют взаимной TLS-аутентификации; если в агенте не заданы cert и key, серверы с TLS 1.2 прерывают рукопожатие оповещением handshake_failure.
SNI отсутствует или указывает неизвестное имя
иногдаПри подключении к IP-адресу без servername или к имени, которое CDN не обслуживает, некоторые фронтенд-серверы отклоняют рукопожатие.
Как исправить
- 1.
Сравните, что поддерживают обе стороны
Выведите список шифров Node.js по умолчанию и перечислите наборы шифров сервера; если пересечений нет, сервер нужно обновить.
bash node -p "require('tls').getCiphers().length + ' suites; default list: ' + require('tls').DEFAULT_CIPHERS" nmap --script ssl-enum-ciphers -p 443 api.example.com # what the server accepts - 2.
Отправьте клиентский сертификат
Задайте cert и key (или pfx) в агенте, через который идут запросы к эндпоинту с mTLS.
js // Mutual TLS: present the client certificate the server requires import https from "node:https"; import fs from "node:fs"; const agent = new https.Agent({ cert: fs.readFileSync("client.pem"), key: fs.readFileSync("client.key") }); https.get("https://mtls.example.com/", { agent }, (res) => res.resume()); - 3.
Владельцам серверов: предлагайте современные наборы шифров
Связка TLS 1.3 и TLS 1.2 с ECDHE и AES-GCM или ChaCha20-Poly1305 работает с любым поддерживаемым релизом Node.js.
text # Server side (nginx): TLS 1.3 + TLS 1.2 ECDHE with AEAD ciphers ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
Связанные ошибки
curl: (35) TLS connect error: error:0A000410:SSL routines::ssl/tls alert handsh…curl (OpenSSL backend)SSL routines:ssl3_read_bytes:ssl/tls alert handshake failureOpenSSL (client side)[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] ssl/tls alert handshake failurePython ssl module (OpenSSL)javax.net.ssl.SSLHandshakeException: (handshake_failure) Received fatal alert:…Java (JSSE)