Error message
code: 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE'
Also printed as
- ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE
The code mirrors OpenSSL's reason string: Node.js releases bundling OpenSSL 3.0 or 3.1 report ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE (from "sslv3 alert handshake failure"); current releases with OpenSSL 3.5 report ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE. With https.request() the same alert appears as "write EPROTO … SSL alert number 40".
Node.js tls.connect / fetch — TLS alert 40 (handshake_failure) received from the server
What it means
The server answered Node.js's ClientHello with TLS alert 40 (handshake_failure) and closed the connection. It found no acceptable combination of cipher suite, key-exchange group and signature algorithm, or it requires a client certificate that was not sent. Node.js derives the error code from OpenSSL's reason text, which is why it contains "SSL/TLS".
Why it happens
No cipher suite in common
commonOld servers limited to RSA key exchange, 3DES or RC4 suites meet a Node.js default cipher list that no longer includes them.
The server requires a client certificate (mTLS)
commonPayment, banking and B2B APIs often require mutual TLS; without cert and key in the agent, TLS 1.2 servers abort with handshake_failure.
Missing or unknown SNI
occasionalConnecting to an IP address without servername, or to a name the CDN does not serve, makes some front ends reject the handshake.
How to fix it
- 1.
Compare what both sides support
Print Node.js's default cipher list and enumerate the server's suites; no overlap means the server must be updated.
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.
Send the client certificate
Configure cert and key (or pfx) on the agent used for the mTLS endpoint.
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.
Server owners: offer current suites
TLS 1.3 plus TLS 1.2 with ECDHE and AES-GCM or ChaCha20-Poly1305 works with every maintained Node.js release.
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;
Related errors
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)