SSL: wrong version number
Yours, and a one-word fix. Your client tried TLS where the other end speaks plain HTTP.
- Usually from
- Your config: the scheme in the proxy URL
- Try first
- Change the proxy URL to http://, for https sites too
What a wrong version number error means through a proxy
Every TLS record starts with a type byte and a version. Send a TLS hello to a port that speaks plain HTTP and the reply is text such as HTTP/1.1 400 Bad Request, whose letters OpenSSL reads as a version it has never heard of. Python shows [SSL: WRONG_VERSION_NUMBER] wrong version number; curl and Node show error:0A00010B:SSL routines::wrong version number. Other TLS libraries and versions say unknown protocol or record layer failure.
The scheme of a proxy URL says how to talk to the proxy, not to the site. https:// asks for TLS to the proxy itself. The gateway expects plain HTTP on its port, and the tunnel it opens to an HTTPS site is encrypted end to end anyway, so the right proxy URL is http:// even when every site you visit is HTTPS.
The same error turns up without any proxy mistake when the address you fetch says https:// but names a port that serves plain HTTP, such as https://example.com:80/. Then the fix is in the URL you are fetching.
Your credentials, the proxy, or the target?
Yours, and a one-word fix. Your client tried TLS where the other end speaks plain HTTP.
Your credentials
Not the cause. The handshake fails before a username or password is sent.
The proxy
Only as the thing your client spoke TLS to. The gateway expects plain HTTP on its port; the
https://in your proxy URL is the mistake.The target site
Only when the URL you fetch says
https://but points at a port that serves plain HTTP. That fails the same way without a proxy.
How to tell
- The error arrives at once, before any status code. A certificate problem names the certificate; this one names a version.
- Print the proxy URL your code actually uses, including
HTTPS_PROXYfrom the environment. Anhttps://at the front is the answer. - In Python, a
ProxyErrorwrappingWRONG_VERSION_NUMBERmeans the handshake with the proxy failed. A bareSSLErrormeans the handshake with the site did. - urllib3 adds
Your proxy appears to only use HTTP and not HTTPS, try changing your proxy URL to be HTTPto the message when it spots this case.
Fixes, in the order to try them
- Costs nothing
Write the proxy URL with http://
Under both the
httpandhttpskeys, in Playwright’sserver, and inHTTP_PROXYandHTTPS_PROXY. Traffic to HTTPS sites stays encrypted inside the tunnel. - Costs nothing
Check the environment and system settings
A stale
HTTPS_PROXY=https://...in a shell profile or CI config applies without appearing in your code. On Windows and macOS, Python also reads the system proxy settings;python -c "import urllib.request; print(urllib.request.getproxies())"shows what it found. - Costs nothing
Check the port in the URL you fetch
An
https://address with a port that serves plain HTTP, such as:80or:8080, fails the same way. Usehttp://for that address, or the port that serves TLS. - Costs nothing
Leave certificate checks alone
verify=Falseand-kchange nothing here: the handshake fails before any certificate is exchanged. Fix the scheme instead. - Costs some time
Add a smoke test after upgrades
Some older client releases quietly spoke plain HTTP to an
https://proxy URL, so an upgrade can surface a typo that was always there. Fix the URL rather than pinning the old release, and send one request through the proxy in CI.
See the real status and headers
Each sample sends HTTPS traffic through an http:// proxy URL, the setup that avoids this error, and shows what the wrong scheme produces.
curl
# wrong: an https:// proxy makes curl do TLS with the gateway itself
curl -s -o /dev/null -x https://USER:[email protected]:8000 https://example.com/
# curl: (35) ... error:0A00010B:SSL routines::wrong version number
# right: plain HTTP to the proxy, TLS to the site inside the tunnel
curl -s -o /dev/null -w 'proxy %{http_connect} site %{http_code}\n' -x http://USER:[email protected]:8000 https://example.com/Python Requests
import os
import requests
for key in ("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"):
value = os.environ.get(key) or os.environ.get(key.lower())
if value:
print(key, "starts with", value.split("://")[0] + "://")
PROXY = "http://USER:[email protected]:8000"
r = requests.get("https://example.com/", proxies={"http": PROXY, "https": PROXY}, timeout=30)
print(r.status_code)With https:// under either key, urllib3 2 raises a ProxyError with WRONG_VERSION_NUMBER inside. The loop prints only the scheme of each variable, so no password reaches your terminal.
Scrapy
RESIDENTIAL = "http://USER:[email protected]:8000"
async def start(self):
for url in self.start_urls:
yield scrapy.Request(url, meta={"proxy": RESIDENTIAL}, errback=self.on_error)
def on_error(self, failure):
if failure.check(NotImplementedError):
self.logger.error("Scrapy cannot use an https:// proxy for an https:// site: %s", failure.value)
elif "wrong version number" in repr(failure.value).lower():
self.logger.error("TLS met plain HTTP: check the scheme and port of %s", failure.request.url)
else:
self.logger.error("%r", failure.value)Write the proxy with http:// in meta, and in http_proxy and https_proxy if you set those.
Playwright
from playwright.sync_api import Error, sync_playwright
PROXY = {"server": "http://resi.proxymonkey.io:8000", "username": "USER", "password": "PASS"}
assert PROXY["server"].startswith("http://"), "the proxy server takes http://, even for https sites"
with sync_playwright() as p:
browser = p.chromium.launch(proxy=PROXY)
page = browser.new_page()
try:
print(page.goto("https://example.com/").status)
except Error as err:
print(err.message.splitlines()[0])
browser.close()
# net::ERR_SSL_PROTOCOL_ERROR an https:// address on a port that speaks plain HTTPNode.js
import { fetch, ProxyAgent } from "undici";
const proxy = new URL(process.env.PROXY_URL ?? "http://USER:[email protected]:8000");
if (proxy.protocol !== "http:") {
throw new Error("use http:// for the proxy URL, not " + proxy.protocol);
}
const dispatcher = new ProxyAgent(proxy.href);
const res = await fetch("https://example.com/", { dispatcher });
console.log(res.status);
// with https:// here: SecureProxyConnectionError, caused by ERR_SSL_WRONG_VERSION_NUMBERThe samples use the residential gateway, resi.proxymonkey.io:8000. For an ISP or datacenter IP, use USER:PASS@IP:PORT for the address you rented. Your dashboard lists the host and port for every order, and where it differs from this page, the dashboard is right.
Will a different proxy line fix it?
When switching helps
No line fixes this. The fix is one word in your config, and it is the same word on every line.
When it will not
A new order with the same https:// in front of it fails the same way.
Is a failed request billed?
We bill for request bytes and response bytes, including headers and protocol overhead on the tunnelled connection. Connections that fail before transferring data are not billed. Retries that you initiate are billed and appear as separate entries in your usage log.
The terms say connections that fail before transferring data are not billed. With the wrong scheme, the handshake with the gateway fails before a tunnel opens or a request is sent. If rows show up in your usage log for these attempts, ask in Discord.
Residential is billed per GB of that traffic. ISP and datacenter addresses are charged per IP for their term, and where a plan includes a traffic allowance, traffic past it is billed per GB under the same rule. The usage log in your dashboard has one row per request with bytes in, bytes out and cost, so you can look up the failed request yourself.
Questions people ask about a wrong version number error
Should the proxy URL be http:// or https:// for HTTPS sites?
http://. The scheme says how you talk to the proxy. Your client then asks the proxy for a tunnel and does TLS with the site inside it, so the site traffic is encrypted either way.
Why did this start after I upgraded requests?
Newer urllib3, which Requests is built on, treats an https:// proxy URL as a request for TLS to the proxy. Some older releases quietly spoke plain HTTP instead. The typo was always there; the upgrade made it visible.
Is WRONG_VERSION_NUMBER a certificate problem?
No. It fails before any certificate is sent. Your client sent a TLS hello and got plain text back, so turning off verification changes nothing.
My proxy URL already says http:// and I still get it. What else?
Look at the URL you are fetching. An https:// address on a port that serves plain HTTP gives the same error, with or without a proxy. Then check HTTPS_PROXY in the environment: Requests uses it for any key you did not set yourself.
Errors that travel with this one
- TLS
SSL certificate errors
Certificate checks fail between you and the site. The proxy passes TLS through untouched, so look at the site and your CA bundle.
Work it out → - ProxyError
Requests ProxyError
Python could not get through the proxy. The outer message never changes; the reason is in the innermost brackets.
Work it out → - TUNNEL_CONNECTION_FAILED
Tunnel connection failed
The proxy answered, but not with the 200 that opens a tunnel. Chromium hides its reason; curl shows it.
Work it out →
Still stuck on a wrong version number error?
Paste your error in the Discord: the full message plus the command or the few lines that set up the proxy, with the password taken out. Someone there has seen it before.
Join the Discord4,200+monkeys in the Discord
Help from humans
Post your error, get an answer. Usually in minutes, usually from someone who has hit the same wall.
A status bot that tells on us
Pool health, incidents and maintenance posted automatically. Including the bad days.
Deals and free traffic
Bonus GB drops, early access to new pools, and the occasional giveaway for a good bug report.