TL;DR
Python can't verify the server's SSL certificate against its trusted CA bundle. Pick the fix that matches your situation:
- macOS: run the
Install Certificates.commandscript that ships with Python. - Any OS with
requests: upgradecertifiand you're likely done. - Corporate/self-signed cert: add the CA certificate to your trust store.
Skip the verify=False shortcut. It silently strips all SSL protection β and opens you to man-in-the-middle attacks in production.
What triggers this error
Every HTTPS request Python makes goes through a certificate check. Python compares the server's cert against a bundle of trusted Certificate Authorities (CAs). That check fails for a few common reasons:
- Python's CA bundle is missing or stale β very common on fresh macOS installs.
- The server uses a self-signed certificate or a private corporate CA.
- A corporate proxy (Zscaler, Charles, Fiddler) is doing SSL inspection and presenting its own cert.
- Python's
sslmodule can't reach the system CA store.
The full traceback looks like this:
requests.exceptions.SSLError: HTTPSConnectionPool(host='example.com', port=443): Max retries exceeded with url: /
Caused by: ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1129)
Fix 1 β macOS: run the certificate installer
Fresh Python installs on macOS skip the system keychain entirely. The installer bundles a one-time fix for this.
Open Terminal and run:
/Applications/Python\ 3.x/Install\ Certificates.command
Swap 3.x for your version β for example, 3.12. Using pyenv or Homebrew? Run this instead:
pip install --upgrade certifi
Then verify Python found a valid bundle:
python -c "import ssl; print(ssl.get_default_verify_paths())"
You want a non-empty cafile or capath pointing to an actual file on disk.
Fix 2 β install or upgrade certifi
certifi ships a curated CA bundle that requests relies on. If it's outdated β or missing β verification fails.
pip install --upgrade certifi
Working in a virtualenv? Activate it first, or you'll upgrade the wrong environment:
source venv/bin/activate
pip install --upgrade certifi
Check which bundle Python will actually use:
python -c "import certifi; print(certifi.where())"
Then run a quick sanity test:
import requests
r = requests.get('https://example.com')
print(r.status_code) # 200 means you're good
Fix 3 β add a custom or corporate CA certificate
Behind a corporate firewall? Tools like Zscaler, Charles Proxy, or your company's internal PKI inject their own CA into every HTTPS connection. Python has never heard of that CA β hence the error.
Get the CA certificate from your IT team (usually a .crt or .pem file), then pick one of these approaches:
Option A: pass the cert path directly in requests
import requests
r = requests.get('https://internal.company.com', verify='/path/to/company-ca.crt')
print(r.status_code)
Option B: set an environment variable
Works for any library that respects REQUESTS_CA_BUNDLE or SSL_CERT_FILE. Add it to your shell profile so it survives reboots.
# Linux / macOS β add to ~/.bashrc or ~/.zshrc
export REQUESTS_CA_BUNDLE=/path/to/company-ca.crt
export SSL_CERT_FILE=/path/to/company-ca.crt
# Windows (PowerShell)
$env:REQUESTS_CA_BUNDLE = "C:\certs\company-ca.crt"
Option C: append the cert to certifi's bundle
Useful when you need a global fix across all scripts in an environment:
import certifi
# Run this once during environment setup
with open('/path/to/company-ca.crt', 'r') as f:
custom_cert = f.read()
certifi_bundle = certifi.where()
with open(certifi_bundle, 'a') as bundle:
bundle.write('\n' + custom_cert)
Note: upgrading certifi later will wipe this change. Re-run the script after upgrades.
Fix 4 β urllib or http.client (no requests)
Not using requests? The standard library needs a manual SSL context:
import urllib.request
import ssl
import certifi
# Build a context backed by certifi's bundle
ctx = ssl.create_default_context(cafile=certifi.where())
with urllib.request.urlopen('https://example.com', context=ctx) as response:
print(response.read())
What NOT to do
Stack Overflow is full of answers that suggest disabling verification:
# DO NOT USE IN PRODUCTION
import requests
requests.get('https://example.com', verify=False) # insecure
# Also bad
import ssl
ssl._create_default_https_context = ssl._create_unverified_context # insecure
Both approaches silence the error β but they do it by turning off the check entirely. Any attacker who intercepts the connection can serve a fake certificate. Python won't complain. Your data leaks.
The only acceptable use is a throwaway test script hitting localhost with no real data. Never against external servers.
Verify the fix worked
Run this snippet to confirm everything is wired up correctly:
python - <<'EOF'
import requests
import ssl
import certifi
print('certifi bundle:', certifi.where())
print('ssl default paths:', ssl.get_default_verify_paths())
r = requests.get('https://httpbin.org/get')
print('HTTP status:', r.status_code) # expect 200
EOF
Still failing after upgrading certifi? The virtualenv might still be pointing at the old version. Check with:
pip show certifi
The Location field must be inside your active virtualenv β not a system Python path like /usr/lib/python3.
Quick decision guide
- macOS, fresh Python install β Fix 1 (run Install Certificates.command)
- Any OS, outdated certs β Fix 2 (upgrade certifi)
- Corporate network / SSL inspection β Fix 3 (add company CA)
- Not using requests β Fix 4 (urllib with certifi context)

