ssl.SSLCertVerificationError: certificate verify failed

⚡ Fastest fix

Python can't trace the server's certificate to a CA it trusts — usually a missing/old CA bundle. Update certifi (and on macOS run the certificate installer). Do not reach for verify=False.

python -m pip install --upgrade certifi

# macOS (python.org build) — install the CA roots:
open "/Applications/Python 3.12/Install Certificates.command"

What you're seeing

requests.exceptions.SSLError:
  HTTPSConnectionPool(host='api.example.com', port=443):
  Max retries exceeded ...
  (Caused by SSLError(SSLCertVerificationError(1,
  '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
   unable to get local issuer certificate (_ssl.c:1006)')))

Read the phrase after "certificate verify failed:". unable to get local issuer certificate = missing CA / intermediate. self-signed certificate = a proxy or a self-signed server. certificate has expired = the server's cert (or a root) is out of date. Each points at a different fix.

30-second triage

Fix 1 — Install CA roots on macOS

When: a python.org build on macOS, failing on every HTTPS site.

# run the installer that ships with the python.org package
open "/Applications/Python 3.12/Install Certificates.command"
# equivalent, in any env:
python -m pip install --upgrade certifi

The macOS python.org installer doesn't use the system keychain; it relies on certifi, and its post-install "Install Certificates" step wires that up. Running it once fixes the blanket failure.

Fix 2 — Update the certifi CA bundle

When: unable to get local issuer certificate against a normal public API.

python -m pip install --upgrade certifi

# confirm which bundle requests/ssl is using
python -c "import certifi; print(certifi.where())"

requests verifies against certifi's bundled roots. An old certifi can miss a newer CA, so upgrading it restores trust for recently issued certificates.

Fix 3 — Trust your corporate proxy's CA

When: it only fails on the company network, VPN, or with antivirus TLS scanning.

# get the corporate root CA (from IT) as a PEM, then point Python at it
export SSL_CERT_FILE=/etc/ssl/corp-root-ca.pem
export REQUESTS_CA_BUNDLE=/etc/ssl/corp-root-ca.pem

# or per call:
requests.get(url, verify="/etc/ssl/corp-root-ca.pem")

A TLS-inspecting proxy re-signs traffic with a private root Python doesn't know. Adding that root to your trust store (env var or verify=) makes verification pass while keeping it on — the safe way, unlike disabling it.

Fix 4 — Supply a missing intermediate certificate

When: a browser accepts the site but Python doesn't, for one host.

# diagnose the chain — "verify error: unable to get local issuer" means
# the server didn't send its intermediate cert
openssl s_client -connect api.example.com:443 -showcerts

# fix the server chain, or bundle the intermediate yourself:
requests.get(url, verify="/path/to/fullchain-including-intermediate.pem")

Browsers cache intermediates and fill gaps; Python doesn't. If the server omits its intermediate certificate, the real fix is on the server (send the full chain) — or provide the intermediate to verify= as a workaround.

The one thing not to do

# ❌ turns off all TLS verification — MITM-vulnerable, hides the real issue
requests.get(url, verify=False)
# ❌ same problem, globally
import ssl; ssl._create_default_https_context = ssl._create_unverified_context

Disabling verification makes the error vanish but exposes you to man-in-the-middle attacks and leaves the trust store broken for everything else. Reserve verify=False for a quick local experiment you'll throw away.

Why this happens

TLS verification works by building a chain of trust: the server's certificate is signed by an intermediate CA, which is signed by a root CA that your system already trusts. Python (via OpenSSL) walks that chain and, if it can't reach a trusted root, raises SSLCertVerificationError: certificate verify failed. Unlike browsers, Python doesn't use the OS keychain by default — requests and the ssl module trust the certifi bundle. So the chain breaks when certifi is missing/outdated (fresh install, old root), when a corporate proxy injects a private root Python never learned, or when the server omits an intermediate that a browser would have cached. Every real fix is about repairing that trust store — which is why turning verification off is a non-answer.

By symptom

Detail in the errorCauseFix
every HTTPS site, new macOS PythonCA roots not installedFix 1
unable to get local issuer, public APIoutdated certifiFix 2
self-signed certificate in chainproxy / AV TLS inspectionFix 3
browser OK, Python fails, one hostserver omits intermediateFix 4
certificate has expiredexpired server cert or old rootupdate cert / certifi

✓ Confirm it's fixed

  • python -c "import requests; requests.get('https://example.com').raise_for_status()" runs clean — with verification on.
  • You did not leave verify=False or an unverified default context anywhere.
  • On a corporate network, other HTTPS hosts also work with the same CA bundle set.

Frequently Asked Questions

What does 'certificate verify failed' mean in Python?

Python's SSL layer could not build a trusted chain from the server's TLS certificate up to a certificate authority (CA) it trusts, so it refused the connection. The most common detail is "unable to get local issuer certificate", meaning Python's CA bundle is missing the issuer — either the CA roots aren't installed, or a proxy is presenting its own certificate.

How do I fix certificate verify failed on macOS?

The python.org macOS installer ships a script that installs CA roots. Run /Applications/Python 3.x/Install Certificates.command (or python -m pip install --upgrade certifi). Fresh macOS Python installs commonly hit this because they don't use the system keychain by default.

Should I use verify=False to fix it?

No, except for a throwaway local test. requests.get(url, verify=False) disables TLS verification entirely, which removes protection against man-in-the-middle attacks and only hides the real problem. Fix the trust store (certifi / OS CA certs) or add the corporate CA instead; keep verification on in anything real.

Why does it fail only behind my company network?

Corporate proxies and antivirus often intercept TLS and present their own root certificate. Python doesn't trust that private CA, so verification fails. The fix is to point Python at the company's CA bundle: set REQUESTS_CA_BUNDLE / SSL_CERT_FILE to a PEM containing the corporate root, or pass verify='/path/to/corp-ca.pem'.

What is 'unable to get local issuer certificate'?

It means the server sent a valid certificate but Python couldn't find the CA that issued it in its trust store — often because the server didn't send the intermediate certificate, or your CA bundle is outdated. Update certifi, and if the server omits intermediates, fix the server chain or supply the intermediate CA to verify=.

How do I point Python at a specific CA bundle?

Set the environment variables SSL_CERT_FILE and REQUESTS_CA_BUNDLE to a PEM file with the needed roots/intermediates, or pass verify='/path/to/ca.pem' to requests. In code you can also use certifi.where() to locate the default bundle and append your CA to a copy of it.

More Python & network errors

Browse the full reference for Python, network, and API errors — exact message, cause, and fix.

All Error References ECONNRESET (Node) pip: externally-managed-environment
About the author

Pasindu Ishan is a software developer based in Sri Lanka. He builds privacy-first developer tools at JSON Dev Tools.