Python Requests: Ignore SSL Certificate Errors, and the Right Way

Rewritten as a tested reference. Every call shown below was executed by the example package against a local HTTPS server with a self-signed certificate; the output blocks are what it printed (see "How the outputs were captured"). Four recipes from the 2024 version were wrong and are shown failing here: passing an ssl.SSLContext as verify=, REQUESTS_CA_BUNDLE="", "verify=False disables verification globally", and the "certificate pinning" example built on the same invalid verify= call.
Updated 2026-10-23: added a section that fetches an HTTPS page through the ScrapingAnt API, run once against BadSSL's self-signed test host.
You called requests.get() and got this:
requests.exceptions.SSLError: HTTPSConnectionPool(host='localhost', port=8443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1010)')))
The two-line way to make it go away:
import requests, urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
response = requests.get("https://localhost:8443/", verify=False)
And the one-line way to fix it instead of hiding it, when you have the server's certificate or your company's CA file:
response = requests.get("https://localhost:8443/", verify="certs/localhost.pem")
The rest of this page shows what each option does, with the real output, including the recipes you will find elsewhere that do nothing or crash in current requests.
ScrapingAnt API
The same fetch through ScrapingAnt
When the site is a public one you only read from, and it also blocks your IP or needs a real browser, you can drop verify=False and fetch it through one ScrapingAnt API call. requests then verifies only the certificate of api.scrapingant.com, and ScrapingAnt connects to the target; that does not make the target trustworthy, so keep verify="path/to/ca.pem" for servers you own. Run against BadSSL's self-signed test host:
import requests
response = requests.get(
"https://api.scrapingant.com/v2/general",
params={"url": "https://self-signed.badssl.com/", "x-api-key": "YOUR_API_KEY", "browser": "false"},
timeout=60,
)
print(response.status_code, response.text.split("<title>")[1].split("</title>")[0])
200 self-signed.badssl.com
Run on 2026-10-23 with requests 2.32.2, with certificate verification left on. browser=false returns the target's response as-is; leave it out to load the page in headless Chrome.
Video Tutorial
The setup, and how the outputs were captured
The example package starts https://localhost:8443/ with a certificate it generates itself (openssl req -x509 … -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost"). Nothing trusts that certificate, so a default request fails exactly as above, and the certificate is only valid for the name localhost, which matters for the hostname-mismatch case later. A few requests go to a public page with a valid certificate to show the default path working. All commands assume the package directory as the working directory, where certs/localhost.pem lives.
The package runs every call through a small helper (show() in _common.py) that prints the return value, or the exception's type and message instead of raising, so one failing line does not stop the script; it also redirects warnings to stdout so they appear next to the result they belong to. The code blocks below show the calls as bare expressions; the output blocks show what the helper printed, with its labels removed. In a file of your own, wrap a call in print() to see its value. If you paste a block into a plain Python file, a failing line raises a normal traceback ending in the same SSLError: … text, and warnings go to stderr with a file:line: prefix.
Option 1: verify=False for one request
import requests
requests.get("https://localhost:8443/", verify=False).status_code
requests.get("https://localhost:8443/", verify=False).status_code # again
requests.get("https://localhost:8443/").status_code # next call, no verify=
InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
200
InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
200
requests.exceptions.SSLError: HTTPSConnectionPool(host='localhost', port=8443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1010)')))
The request succeeds and urllib3 warns once for each unverified request. verify=False is per request: the third call, without it, fails as before. That is the first thing the 2024 version of this article got wrong. (On requests older than 2.32.0 there was a real leak: the first verify=False request on a Session switched verification off for later requests to the same origin, CVE-2024-35195. If you are pinned below 2.32.0, upgrade before relying on per-request verify.)
Option 2: silence the warning
import requests, urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
requests.get("https://localhost:8443/", verify=False).status_code
200
disable_warnings calls warnings.simplefilter("ignore", …) for that category, so it is process-wide. Call it once at import time in scripts where you have decided to skip verification; in a library, do not call it at all, the warning belongs to whoever runs the code.
Option 3: Session.verify = False for every request in a session
The supported way to switch verification off for many requests is a Session:
import requests, urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
s = requests.Session()
s.verify = False
s.get("https://localhost:8443/").status_code
s.get("https://localhost:8443/index.html").status_code
s.get("https://localhost:8443/", verify=True).status_code
200
200
requests.exceptions.SSLError: HTTPSConnectionPool(host='localhost', port=8443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1010)')))
The third line shows the precedence rule: a verify argument on the call wins over the session setting, so verify=True on one request restores verification for that request. requests has no module-level verify setting: requests.get() and friends build a fresh Session for every call (requests/api.py), so if your code base calls them from many places, route those calls through one module-level Session (or a thin wrapper) and set verify there.