SSL certificate errors
Yours or the site’s. The proxy does not touch TLS on a tunnelled HTTPS request.
- Usually from
- Your CA bundle, or the site’s certificate
- Try first
- Update your CA bundle, then check which CA your tool trusts
What a certificate error means through a proxy
Verification errors (CERTIFICATE_VERIFY_FAILED, ERR_CERT_AUTHORITY_INVALID, unable to get local issuer certificate) mean your client did not trust the certificate it was shown. Each tool checks against its own CA bundle: Requests uses certifi, Node ships its own list, curl uses whichever bundle its build points at, and Chrome uses its own root store.
An error on one site only is that site’s certificate: expired, missing an intermediate, or issued for another name. Browsers can fetch a missing intermediate on their own, which is why a site can look fine in Chrome and fail in a script.
An error on every site, with an issuer you do not recognise, means something on your side re-signs TLS: a company firewall or an antivirus that inspects HTTPS. Your tool needs that CA added to the list it trusts.
WRONG_VERSION_NUMBER is a different error, a handshake with the wrong kind of server, usually an https:// proxy URL. It has its own page.
Your credentials, the proxy, or the target?
Yours or the site’s. The proxy does not touch TLS on a tunnelled HTTPS request.
Your credentials
Not the cause. Credentials are checked on the CONNECT, before any TLS.
The proxy
Not the cause: it passes TLS through untouched and never presents a certificate of its own on a tunnelled request.
The target site
An expired certificate, a missing intermediate or a name mismatch. It fails the same way without the proxy.
How to tell
- The issuer in the error or in
curl -vis your employer or your antivirus, not a public CA: something on your side is inspecting TLS. - The same error with and without the proxy: it is the site or your CA bundle.
openssl s_clientagainst the site, without the proxy, shows its chain and expiry directly.- Only one tool on the machine fails: that tool carries its own, older CA bundle.
Fixes, in the order to try them
- Costs nothing
Update the CA bundle
pip install -U certififor Python, a current Node release for Node, and your OS updates orupdate-ca-certificatesfor curl. - Costs nothing
Check which bundle your tool loads
Requests reads
REQUESTS_CA_BUNDLEandCURL_CA_BUNDLEbefore certifi, so a variable left pointing at an old file overrides an update. On OpenSSL builds,curl -vprints the CAfile it used. - Costs nothing
Look at the site’s certificate
Check its expiry, the names it covers and whether the chain is complete. A broken chain is the site owner’s to fix; you can tell them.
- Costs nothing
Disable verification for one test run only
-k,verify=Falseandignore_https_errorsconfirm a diagnosis. Left in a script, they remove the only check that the page came from the real site. - Costs some time
Point your tool at your network’s CA
If your machine sits behind a firewall that inspects TLS, your tool needs that firewall’s CA:
REQUESTS_CA_BUNDLE,NODE_EXTRA_CA_CERTSor--cacert.
See the real status and headers
Each sample shows the handshake with the site, or the exact certificate error, with verification left on.
curl
# -v shows the tunnel opening, then the TLS handshake with the site
curl -sv -o /dev/null -x http://USER:[email protected]:8000 https://example.com/ 2>&1 \
| grep -E 'CONNECT|HTTP/1.1 200|CAfile|SSL|TLS|subject|expire|issuer'
# the site's certificate, checked without the proxy
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -enddatePython Requests
import os
import certifi
import requests
PROXY = "http://USER:[email protected]:8000"
print("CA bundle:", os.environ.get("REQUESTS_CA_BUNDLE") or os.environ.get("CURL_CA_BUNDLE") or certifi.where())
try:
r = requests.get("https://example.com/", proxies={"http": PROXY, "https": PROXY}, timeout=30)
print(r.status_code)
except requests.exceptions.SSLError as err:
print(err)The first line prints the bundle Requests checks the site against, in the order Requests looks for it.
Scrapy
# settings.py: Scrapy skips certificate checks unless this is on (2.15+)
DOWNLOAD_VERIFY_CERTIFICATES = True
# in the spider
from scrapy.exceptions import DownloadFailedError
async def start(self):
for url in self.start_urls:
yield scrapy.Request(url, errback=self.on_error)
def on_error(self, failure):
if failure.check(DownloadFailedError) and "certificate verify failed" in str(failure.value):
self.logger.error("%s: the certificate did not verify: %s", failure.request.url, failure.value)
else:
self.logger.error("%s: %r", failure.request.url, failure.value)With verification on, Scrapy checks against the CA paths of its OpenSSL, not certifi. SSL_CERT_FILE points it at another bundle.
Playwright
from playwright.sync_api import Error, sync_playwright
PROXY = {"server": "http://resi.proxymonkey.io:8000", "username": "USER", "password": "PASS"}
with sync_playwright() as p:
browser = p.chromium.launch(proxy=PROXY)
page = browser.new_page()
try:
page.goto("https://example.com/")
except Error as err:
print(err.message.splitlines()[0])
browser.close()
# net::ERR_CERT_DATE_INVALID the certificate has expired
# net::ERR_CERT_COMMON_NAME_INVALID issued for a different name
# net::ERR_CERT_AUTHORITY_INVALID not signed by a CA Chromium trustsNode.js
import { fetch, ProxyAgent } from "undici";
const dispatcher = new ProxyAgent("http://USER:[email protected]:8000");
try {
await fetch("https://example.com/", { dispatcher });
} catch (err) {
console.error(err.cause?.code, err.cause?.message);
}
// CERT_HAS_EXPIRED the certificate has expired
// ERR_TLS_CERT_ALTNAME_INVALID issued for a different name
// UNABLE_TO_VERIFY_LEAF_SIGNATURE the site's chain is incomplete
// behind a TLS-inspecting firewall: NODE_EXTRA_CA_CERTS=./ca.pem node tls.mjsThe 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
Switching line does not change which certificate your client checks. On an HTTPS site it is always the site’s own.
When it will not
Buying ISP or datacenter to get rid of a certificate error changes the address the site sees. The certificate check still happens on your machine, 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 count protocol overhead on the tunnelled connection as billed bytes, and say connections that fail before transferring data are not billed. A handshake that fails inside an open tunnel has moved some bytes, and the terms do not describe that case on its own. Check the row in your usage log and ask in Discord if it looks wrong.
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 certificate error
Can the proxy see my HTTPS traffic?
No. For an HTTPS site the proxy opens a tunnel and passes encrypted bytes through. It sees the host and port you asked for and how many bytes moved, which is what the meter counts. It cannot read the pages or swap the certificate.
Why does the site work in my browser and fail in Python?
Browsers ship their own trust store and can fetch a missing intermediate certificate by themselves. Python uses the certifi bundle and fails where the site’s chain is incomplete or the bundle is old. Update certifi, then check the site’s chain.
Is verify=False safe?
For one test run to confirm the diagnosis, it does no harm. Left in a scraper, it means anyone in the path could hand you a fake page and your code would accept it.
Why does every site fail on one network and work on another?
Something on that network is re-signing TLS, usually a company firewall or an antivirus that scans HTTPS. Your browser may trust its CA because it was installed on the machine, while your script uses its own bundle and does not. Ask for the CA file and point your tool at it rather than turning verification off.
Errors that travel with this one
- WRONG_VERSION_NUMBER
SSL: wrong version number
Your client spoke TLS to something speaking plain HTTP. Almost always an https:// proxy URL that should say http://.
Work it out → - ECONNREFUSED
Connection refused
Something said no before any HTTP happened. Usually the host or port you gave for the proxy.
Work it out → - TIMEOUT
Connection timeout
Nothing came back in time. Find out which leg stalled before you raise the limit.
Work it out →
Still stuck on a certificate 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.