<!-- https://pkizerotohero.com/errors/python-ssl-wrong-version-number/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> Fix Python's [SSL: WRONG_VERSION_NUMBER] wrong version number: find what answers instead of TLS, then fix the port, the proxy URL or the SMTP setup.

Source: https://pkizerotohero.com/errors/python-ssl-wrong-version-number/ · Last reviewed: 2026-10-20

- Python
- Exact text checked with the real tool

# \[SSL: WRONG\_VERSION\_NUMBER\] wrong version number

Also seen in: requests, urllib3, pip, httpx, aiohttp, smtplib, imaplib

**In one sentence:**

Python began a TLS handshake but got no TLS reply: usually an https URL for a plain-HTTP port, an https:// proxy URL, or SMTP\_SSL on a STARTTLS port.

[Check yours: Paste your error](https://pkizerotohero.com/tools/error-finder/)

## Where you see it

In Python’s `ssl` module and everything built on it: urllib, requests, urllib3, pip, httpx, aiohttp and smtplib. The text in brackets is OpenSSL’s reason code; the number after `_ssl.c:` changes with the Python version.

```text
ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1028)
```

Python linked with most OpenSSL 3.2 to 3.6 releases reports the same situation as `[SSL] record layer failure`, for example `ssl.SSLError: [SSL] record layer failure (_ssl.c:1032)`. OpenSSL 3.3.7, 3.4.5, 3.5.6, 3.6.2, 4.0 and later say `wrong version number` again.

requests wraps it:

```text
requests.exceptions.SSLError: HTTPSConnectionPool(host='api.example.com', port=8080): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1028)')))
```

## What it means

Python sent the first message of a [TLS handshake](https://pkizerotohero.com/handbook/glossary/#tls-handshake) and expected a TLS record back. The reply did not look like TLS at all: often it was a plain-HTTP answer or a mail server’s greeting, whose first bytes OpenSSL reads as a nonsense TLS version. Despite the name, this is almost never about TLS versions. Your certificates and CA bundle are not involved; the connection went to something that does not speak TLS on that port.

## Likely causes

### An https URL for a port that speaks plain HTTP

A development server, an internal service or a health endpoint that listens with plain HTTP, called with `https://`. **Confirm:** a plain request works, and OpenSSL shows the same reason:

```bash
curl -v http://api.example.com:8080/
openssl s_client -connect api.example.com:8080 -servername api.example.com </dev/null
```

**Fix:** use `http://` for that service if it is on a trusted local network, or, better, enable TLS on it, or use the port where it does speak TLS (often 443 or 8443).

### The proxy URL starts with https://

A very common cause behind company proxies. `HTTPS_PROXY=https://proxy.example.com:3128` tells Python to speak TLS **to the proxy**, but most proxies expect plain HTTP there, even for HTTPS traffic. Recent urllib3 versions say so directly:

```text
requests.exceptions.ProxyError: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/ (Caused by ProxyError('Unable to connect to proxy. Your proxy appears to only use HTTP and not HTTPS, try changing your proxy URL to be HTTP. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#https-proxy-error-http-proxy', SSLError(SSLError(1, '[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1028)'))))
```

pip shows the same inside a `WARNING: Retrying …` line. **Confirm** the proxy settings:

```bash
env | grep -i proxy
pip config list
```

```powershell
Get-ChildItem Env: | Where-Object Name -like '*proxy*'
```

**Fix:** use `http://` in the proxy URL; the traffic to the website stays encrypted end to end inside the proxy tunnel:

```bash
export HTTPS_PROXY=http://proxy.example.com:3128
```

If the proxy also inspects TLS, you will next need its root certificate: [How to work behind a TLS-inspecting corporate proxy, safely](https://pkizerotohero.com/guides/corporate-proxy-certificates/).

### SMTP\_SSL on a STARTTLS port

Mail servers use two styles. Port 465 speaks TLS from the first byte (`smtplib.SMTP_SSL`). Port 587, and 25, start in plain text and switch to TLS with the STARTTLS command (`smtplib.SMTP` plus `starttls()`). `SMTP_SSL` on port 587 reads the server’s plain-text greeting as a TLS record and fails with this error.

**Fix:** match the class to the port:

```python
import os, smtplib, ssl

context = ssl.create_default_context()
with smtplib.SMTP("smtp.example.com", 587) as smtp:
    smtp.starttls(context=context)
    smtp.login("user@example.com", os.environ["SMTP_PASSWORD"])
```

The same applies to `imaplib.IMAP4_SSL` (port 993) versus `IMAP4` with `starttls()` (port 143). See the [smtplib documentation](https://docs.python.org/3/library/smtplib.html).

### Something in between answers instead

A captive portal, firewall or proxy may answer with its own plain-text or HTTP block page. If the same code works on another network, this is the cause; talk to the network’s owner.

Warning

verify=False does not help here

`verify=False` and an unverified SSL context only skip certificate checks, which this error never reaches. They do not fix a wrong port or proxy URL, and they leave a hole for later. Fix the URL, the proxy setting or the SMTP class.

## Check yours

*Interactive tool: Paste your error (runs in the browser at [/tools/error-finder/](https://pkizerotohero.com/tools/error-finder/))*

Paste the full traceback, including any `ProxyError` text. The finder shows the matching page, so you can tell a protocol mix-up from a certificate problem.

## Learn why

- [The TLS 1.3 handshake](https://pkizerotohero.com/handbook/tls-handshake/): what the first TLS message looks like, and why other protocols break it.
- The same mistake in other tools: [curl: (35)](https://pkizerotohero.com/errors/curl-35-ssl-connect-error/) and [ERR\_SSL\_PROTOCOL\_ERROR](https://pkizerotohero.com/errors/chrome-err-ssl-protocol-error/).
- Certificate problems in Python are different: [CERTIFICATE\_VERIFY\_FAILED](https://pkizerotohero.com/errors/python-certificate-verify-failed/).

**Early edition.** Checked against the standards; an independent expert review is in progress. Found a mistake? [Tell us](https://pkizerotohero.com/contact/?topic=content-error&page=/errors/python-ssl-wrong-version-number/).

Last reviewed 20 October 2026Edition 0.9

## Learn why it happens

- [Chapter 15: The TLS 1.3 handshake](https://pkizerotohero.com/handbook/tls-handshake/)

## Everything on this topic

- [TLS and HTTPS](https://pkizerotohero.com/topics/tls/)
