Python requests SSL: CERTIFICATE_VERIFY_FAILED fix
Happens when Python can't verify a server's SSL cert. Usually a missing or outdated CA bundle on your system. Here's the real fix.
You're writing a script that hits an HTTPS API — maybe GitHub, maybe an internal service. Everything looks right: the URL, the headers, the payload. Then requests.get('https://api.example.com') blows up with:
requests.exceptions.SSLError: HTTPSConnectionPool(host='api.example.com', port=443):
Max retries exceeded with url: /
(Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1129)')))
You check the URL in a browser — works fine. Your coworker's script runs fine. What's actually happening here is that Python's requests library, via urllib3, is trying to verify the server's SSL certificate against a local CA (Certificate Authority) bundle. If that bundle is missing, outdated, or Python can't find it, you get this error. The server's certificate is fine — your Python environment is the problem.
Root cause
requests uses certifi under the hood to provide a CA bundle. On a fresh Python install — especially on Windows or a custom-built Python on macOS — certifi might not be installed, or the bundled certificates are old. On Linux, Python typically uses the system's ca-certificates package, but if that's missing or corrupted, same error.
The second common trigger: corporate proxies or antivirus software that perform SSL inspection. They replace the server's cert with their own CA-signed cert, and Python doesn't trust that internal CA. Browsers do because they're configured with the company's root CA — Python is not.
The fix
Skip the quick-and-dirty verify=False. That disables SSL verification entirely — you're vulnerable to MITM attacks. Here's the proper fix.
Step 1: Install or update certifi
Open a terminal and run:
pip install --upgrade certifi
This installs the latest Mozilla CA bundle. The reason this works: requests will automatically use certifi.where() as the default CA bundle path if no other path is specified.
Step 2: Verify the bundle path
Run this in a Python shell to confirm Python knows where the bundle is:
import certifi
print(certifi.where())
You should see a path like /usr/local/lib/python3.11/site-packages/certifi/cacert.pem or similar. If this file doesn't exist, something went wrong with the install — check your Python environment.
Step 3: Test with explicit verify
Try passing the certifi path explicitly, just to be sure:
import certifi
import requests
response = requests.get('https://api.github.com', verify=certifi.where())
print(response.status_code)
If this works but your normal requests.get() still fails, you've got a stale certifi install or an environment variable overriding the CA path.
Still failing? Check these
Corporate SSL inspection
If you're behind a corporate proxy that does SSL decryption, you need to add your company's root CA to Python's trusted store. Get the .pem file from your IT department or export it from your browser, then append it to the certifi.where() file. Or set the REQUESTS_CA_BUNDLE environment variable to point to a custom bundle that includes both Mozilla's CAs and your company's CA:
export REQUESTS_CA_BUNDLE=/path/to/combined-ca-bundle.pem
On Windows, use set instead of export.
Outdated Python or requests
Python 3.6 and older versions of requests (pre-2.25) had weaker SSL handling. Upgrade both:
pip install --upgrade requests
If you're stuck on an old Python version due to a legacy project, consider using urllib3 directly with a custom SSLContext, but honestly, upgrade your Python if you can.
macOS specific: Python from python.org vs Homebrew
macOS's built-in Python (if you're using it) doesn't include SSL certificates. The Python.org installer for macOS includes a Install Certificates.command script. Run it from /Applications/Python 3.x/. Homebrew-installed Python typically handles this automatically — but check that openssl is installed and linked properly:
brew install openssl
brew link --force openssl
Docker containers
If this happens inside a Docker container, the base image probably lacks CA certificates. Add this to your Dockerfile:
RUN apt-get update && apt-get install -y ca-certificates
Or for Alpine-based images:
RUN apk add --no-cache ca-certificates
Then rebuild. The reason this matters: Alpine images are minimal — they don't ship with any CA bundle by default.
One last check
If you've done all this and the error persists, check for environment variables that override the CA path:
echo $SSL_CERT_FILE
echo $REQUESTS_CA_BUNDLE
If either points to a non-existent file, unset it. requests will check these variables before falling back to certifi. A wrong path there will break things silently.
Was this solution helpful?