Error message
[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] ssl/tls alert handshake failure
Also printed as
- [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure
The reason code stays SSLV3_ALERT_HANDSHAKE_FAILURE, but the text depends on OpenSSL: "ssl/tls alert handshake failure" with OpenSSL 3.3 and later, "sslv3 alert handshake failure" with older versions. A source location such as "(_ssl.c:1082)" follows.
Python ssl module (OpenSSL) — ssl.SSLError, TLS alert 40 (handshake_failure) from the server
What it means
The server aborted the handshake with TLS alert 40 (handshake_failure). It could not find acceptable parameters in Python's ClientHello, or it required a client certificate that was not presented. The alert itself carries no detail, so the cause has to be found by comparing what each side supports or by reading the server's log.
Why it happens
No cipher suite in common
commonSince Python 3.10 the default context offers no suites without forward secrecy and none with SHA-1 MACs, at OpenSSL security level 2. Legacy servers limited to RSA key exchange, CBC-SHA or 3DES suites have nothing in common with it.
The server requires a client certificate
commonMutual-TLS endpoints (banking and B2B APIs, internal services) answer a handshake without a client certificate with this alert on TLS 1.2.
SNI missing or not configured on the server
occasionalConnecting by IP without server_hostname, or to a name the CDN or proxy does not serve, makes some servers abort with handshake_failure.
How to fix it
- 1.
Compare the cipher lists
Print what this Python offers and compare it with the server's list (for example from nmap --script ssl-enum-ciphers). No overlap means the server needs an update.
python import ssl ctx = ssl.create_default_context() print(sorted(c["name"] for c in ctx.get_ciphers())) # what this Python offers (TLS 1.2 names) - 2.
Present the client certificate
For mTLS APIs, load the client certificate and key into the context with load_cert_chain(); requests takes cert=("client.pem", "client.key").
python import ssl, urllib.request ctx = ssl.create_default_context() ctx.load_cert_chain(certfile="client.pem", keyfile="client.key") # mutual TLS urllib.request.urlopen("https://mtls.example.com/", context=ctx) - 3.
Server owners: offer current suites
TLS 1.3 plus TLS 1.2 ECDHE with AES-GCM or ChaCha20-Poly1305 is accepted by every maintained client.
text # Server side: offer TLS 1.3 and TLS 1.2 ECDHE + AEAD suites 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;