SSLError Through a Proxy in requests and urllib3

A secure connection was not established: either the program tried to speak TLS to a proxy that expects plain HTTP, or the site's certificate failed verification.

Updated · Prokso editorial team

Key takeaways
  • Wrong version number through a proxy almost always means the https:// scheme in an HTTP proxy line
  • CERTIFICATE_VERIFY_FAILED concerns the site's certificate: the proxy does not change encryption in a CONNECT tunnel
  • Write http:// for port 823, update certifi and do not turn off verification with verify=False

What the error means

When requests opens an https site through an HTTP proxy, it first asks the proxy to open a tunnel with the CONNECT method. After that the proxy only passes bytes along, and the encrypted connection is established between your code and the site. So the certificate that requests verifies was sent by the site, not by the proxy.

The most common mistake here is a proxy line with the https:// scheme. Then urllib3 tries to establish TLS with the proxy itself, while port 823 expects plain HTTP. What comes back is not TLS, and OpenSSL reports [SSL: WRONG_VERSION_NUMBER] wrong version number. In current urllib3 2.x this is wrapped in a ProxyError with the hint Your proxy appears to only use HTTP and not HTTPS, try changing your proxy URL to be HTTP.

CERTIFICATE_VERIFY_FAILED is a different situation: TLS with the site started, but the certificate chain failed verification. Through a CONNECT tunnel the causes are the same as without a proxy: an outdated set of root certificates, a certificate substituted by an antivirus or a corporate network, or the REQUESTS_CA_BUNDLE or CURL_CA_BUNDLE variables pointing to a different file.

Causes

  • The https:// scheme for an HTTP proxy. The line https://USERNAME:PASSWORD@gw.prokso.com:823 makes the library encrypt the connection to the proxy, and you get wrong version number.
  • The http:// scheme on the SOCKS5 port. Port 824 only speaks the SOCKS5 protocol. An HTTP request to it gets no HTTP response, and the error text is confusing.
  • HTTPS interception. An antivirus or a corporate gateway substitutes its own certificate, and requests reports CERTIFICATE_VERIFY_FAILED, often with the text self-signed certificate in certificate chain.
  • An outdated certifi or a different CA file. Requests takes root certificates from the certifi package, and the REQUESTS_CA_BUNDLE variable can replace that set.
  • A problem with the site's own certificate. The certificate has expired or was issued for a different domain. In that case the error also happens without the proxy.

How to fix it

  1. For port 823 and ports 10000-20000 in HTTP mode, write the http:// scheme: 'https': 'http://USERNAME:PASSWORD@gw.prokso.com:823'. The 'https' key in the dictionary is the scheme of the site, not of the proxy.
  2. For SOCKS5, use socks5h://USERNAME:PASSWORD@gw.prokso.com:824 and install the SOCKS dependencies. Examples for both protocols are in the Python requests guide, and all ports and line formats are in the gateway reference.
  3. Update the packages with pip install -U requests certifi and check whether REQUESTS_CA_BUNDLE and CURL_CA_BUNDLE are set: env | grep CA_BUNDLE.
  4. If you work in a corporate network or with an antivirus that scans HTTPS, pass the path to their root certificate: verify='/path/to/ca.pem'.
  5. Do not set verify=False. According to the requests documentation, this option only applies to the site's certificate: it will not remove wrong version number from the proxy, and it only hides CERTIFICATE_VERIFY_FAILED, leaving the connection open to substitution.

How to check that it works

Run requests.get('https://prokso.com/ip', proxies=proxies, timeout=30) with certificate verification turned on. If you get JSON with the proxy IP, the tunnel and TLS work. For comparison, open the My IP page in a browser through the same proxy: if there is no error there but there is one in code, the problem is in the Python setup.

Two-minute diagnosis

First paste the line into the proxy checker. If the proxy works there, the cause is in your app or its settings. If not, test the line from a terminal:

curl -v -x "http://USERNAME:PASSWORD@gw.prokso.com:823" https://api.ipify.org
  • A < HTTP/1.1 407 line right after CONNECT means the proxy answered. The name after the code, for example NO_USER, points to the cause.
  • The proxy answered CONNECT with 200 and then another code arrived - the proxy works, and that answer came from the website.
  • No answer at all, only a timeout - the connection never reached the proxy: check the host, port and network.

Setup guides where this error comes up

FAQ

Can the Prokso proxy substitute the site's certificate?

No. In a CONNECT tunnel, encryption is established between your program and the site, and the proxy passes data that is already encrypted. More details are on the <a href="/en/errors/ssl-certificate-errors">SSL and certificate errors</a> page.

The site is https://, so should the proxy be https:// too?

No. The scheme of the site address and the scheme of the proxy line are different things. The site can be on https://, while the connection to the proxy on port 823 is plain http://, with the encrypted tunnel running inside it.

Why does the error only appear in Python while the browser opens the site?

The browser has its own certificate store, while requests uses the certifi package or the file from the REQUESTS_CA_BUNDLE variable. Update certifi and check the environment variables.

Residential $1.59, mobile $2.50, datacenter $0.65 per GB

Sign up in a minute, get access right after payment.

Create account