How to Fix EPROTO (SSL Wrong Version Number)
Find out whether a proxy URL or a port caused it, then fix it in Python, Node.js or cURL.
Updated
TL;DR
EPROTO with wrong version number means your client opened a TLS handshake and the proxy or server answered in plain text, so the handshake stopped at the first reply. The fastest fix, when a proxy is set, is to start its URL with http://, both in code and in HTTPS_PROXY.
What EPROTO looks like in each client
Seen on the versions named. The wording changes with the OpenSSL version, and macOS's built-in curl reports a protocol version alert.
| Where | What you see |
|---|---|
| Node.js 20.17 and 20.19 https module (OpenSSL 3.0) | Error: write EPROTO 80E1A9ED01000000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../deps/openssl/openssl/ssl/record/ssl3_record.c:355: |
| Node.js 24.22 fetch() (OpenSSL 3.5.8), in err.cause (code ERR_SSL_WRONG_VERSION_NUMBER; the thrown error is TypeError: fetch failed) | Error: 80E1A9ED01000000:error:0A00010B:SSL routines:tls_validate_record_header:wrong version number:../deps/openssl/openssl/ssl/record/methods/tlsany_meth.c:78: |
| Node.js 24.11 https module (OpenSSL 3.5.4) | Error: write EPROTO 80E1A9ED01000000:error:0A0000C6:SSL routines:tls_get_more_records:packet length too long:../deps/openssl/openssl/ssl/record/methods/tls_common.c:663: |
| npm 11 with an https:// proxy | npm error code EPROTO |
| Python 3.12 urllib (OpenSSL 3.0) | urllib.error.URLError: <urlopen error [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1000)> |
| pip 24 with an https:// proxy (its bundled urllib3 1.26, the library under requests) | ProxyError('Your proxy appears to only use HTTP and not HTTPS, try changing your proxy URL to be HTTP. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#https-proxy-error-http-proxy', SSLError(SSLError(1, '[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1000)'))) |
| curl 8.7 on macOS (system build with LibreSSL 3.3.6) | curl: (35) LibreSSL/3.3.6: error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version |
Why this happens
Your client spoke TLS, but the other end replied in plain text.
A common case is a proxy URL that starts with https:// for a proxy port that speaks plain HTTP. OpenSSL reads the reply's first five bytes as a TLS record header and finds no valid version there.
Diagnose your EPROTO first
Each check points to one cause: the proxy URL, a plain-HTTP port or a TLS version mismatch.
Open the same address with http:// in a browser; if the page loads, that port serves plain HTTP, not TLS.
Run env | grep -i _proxy; an HTTPS_PROXY or ALL_PROXY value starting with https:// makes your tools open TLS with the proxy itself.
With no proxy variables set, run python -c 'import urllib.request; print(urllib.request.getproxies())'; an https:// value comes from system proxy settings.
Run curl -v through the proxy; a TLS Client hello before any CONNECT line means curl is handshaking with the proxy, not the site.
Read the reason after the code; unsupported protocol instead means the server does speak TLS, but in a version your client refuses.
Solutions ranked by effectiveness
Fix the proxy URL first; the other two cover plain-HTTP ports and mail servers.
- Most common fix
Start the proxy URL with http://
Applies when only proxied requests fail and the provider documents an http:// address. Use http:// in code, HTTPS_PROXY and npm's https-proxy; the CONNECT tunnel still carries end-to-end TLS.
# pip install requests import requests proxy = "http://USERNAME:PASSWORD@proxy.geonode.io:9000" proxies = {"http": proxy, "https": proxy} r = requests.get("https://example.com/", proxies=proxies, timeout=30) print(r.status_code) - Check next
Point https:// at a TLS port
Applies when the error appears without a proxy, because the port you call answers in plain HTTP, such as a local dev server started without a certificate.
Use http:// for a local server that has no certificate.
Use https:// only on the port your API documents; with no port, it means 443.
For HTTPS on your own server, enable TLS there and call its TLS port.
- For developers
Match implicit TLS to the mail port
Applies to SMTP clients. Port 587 opens in plain text and upgrades with STARTTLS, so a client that starts TLS at once reads the server's 220 greeting as a bad record.
In Nodemailer, set secure: true only for port 465; leave it false for 587.
In Python, use smtplib.SMTP_SSL on 465, and SMTP plus starttls() on 587.
Stop the EPROTO from coming back
Habits that keep schemes, ports and TLS modes paired correctly as configs and runtimes change.
Validate proxy URLs at startup and reject https:// unless your provider documents a TLS proxy port.
In Node.js, key monitoring on the EPROTO code and the ERR_SSL_ prefix, not message text, which changes with OpenSSL.
Write in your setup docs that HTTPS_PROXY names the proxy for https:// sites, not one reached over TLS.
Store mail settings as one URL, such as smtps://host:465 in Nodemailer, so port and TLS mode cannot drift apart.
Related errors
Learn more
FAQ
Endpoints with matching ports
Change the protocol in Geonode's Endpoint Generator, and each endpoint's port updates to match.


