Error message
[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled
Appears with Python linked against OpenSSL 3.0 or later (most Python builds since 2022); OpenSSL 1.1.1 still allowed these servers. A source location such as "(_ssl.c:1082)" follows.
Python ssl module (OpenSSL 3) — ssl.SSLError, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED
What it means
The server negotiated TLS 1.2 or older without the secure renegotiation extension of RFC 5746. OpenSSL 3 clients refuse such servers by default, because legacy renegotiation allowed a man-in-the-middle to splice its own data in front of the client's request (CVE-2009-3555). Python inherits that default, so the connection fails during the handshake.
Why it happens
An outdated server or appliance TLS stack
commonOld Windows Server releases, legacy Java servers, ERP systems, network devices and some government or banking endpoints still run TLS implementations that never got RFC 5746.
A TLS-terminating middlebox without RFC 5746
commonOld load balancers and proxies (some F5 BIG-IP and corporate proxy versions) terminate TLS for modern back ends and present their own legacy behaviour to clients.
The client moved to OpenSSL 3
commonThe same server worked with Python builds on OpenSSL 1.1.1; upgrading Python, the OS or a container base image switched OpenSSL and its stricter default.
How to fix it
- 1.
Confirm the server lacks secure renegotiation
openssl s_client reports "Secure Renegotiation IS NOT supported" for such servers.
bash openssl s_client -connect legacy.example.com:443 -servername legacy.example.com </dev/null 2>/dev/null \ | grep -i 'secure renegotiation' # "Secure Renegotiation IS NOT supported" confirms the server lacks RFC 5746 - 2.
Fix the server
Updating the server's TLS stack or firmware, or offering TLS 1.3, removes the problem for every client.
text Server / appliance owners: update the TLS stack or firmware to any release supporting RFC 5746 (every mainstream TLS library since 2010), or enable TLS 1.3, which has no renegotiation at all. - 3.
Temporary workaround: allow it for one host only
ssl.OP_LEGACY_SERVER_CONNECT (Python 3.12+) re-enables connections to such servers for this one context. It re-opens the renegotiation splicing risk for that host, so do not set it globally or via the system OpenSSL configuration.
python # Temporary workaround for one legacy host only (Python 3.12+: ssl.OP_LEGACY_SERVER_CONNECT) import ssl, urllib.request ctx = ssl.create_default_context() ctx.options |= ssl.OP_LEGACY_SERVER_CONNECT urllib.request.urlopen("https://legacy.example.com/", context=ctx)