The ContextFew things stall a Python project faster than a sudden SSL handshake failure. You might be calling a REST API like Stripe or Twilio, and while everything worked on your local Linux machine, the script crashes the moment you move to macOS or a restricted corporate network. This happens because Python cannot find a valid set of root certificates to verify the identity of the server you are trying to reach.
Think of it as a security checkpoint. Your script sees the server's ID card (the SSL certificate), but it doesn't recognize the agency that issued it. Without a trusted local list of these agencies, Python refuses to connect to prevent potential security leaks.
Environment & Error MessageThis error is most common in Python 3.6+ on macOS Ventura or Sonoma. It also plagues developers on Windows or Linux when working behind corporate firewalls—like Zscaler or Cisco Umbrella—that perform SSL inspection by re-signing traffic with internal certificates.
The exact error usually looks like this:
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1123)>
Debugging the Root CauseBefore trying every fix on StackOverflow, check exactly where Python is looking for its trust store. Run this snippet in your terminal or IDE:
import ssl
import os
print("Default Verify Paths:", ssl.get_default_verify_paths())
print("SSL_CERT_FILE:", os.environ.get('SSL_CERT_FILE'))
print("SSL_CERT_DIR:", os.environ.get('SSL_CERT_DIR'))
If openssl_cafile is None or points to a path that doesn't exist, you've found the problem. Python is essentially flying blind.
Solutions### 1. The macOS Specific FixIf you installed Python via the official installer from python.org, it does not use the macOS system keychain. Instead, it uses its own internal OpenSSL version which ships with zero certificates. You can fix this in five seconds by running the command script bundled with your installation:
# Change 3.11 to match your specific version
/Applications/Python\ 3.11/Install\ Certificates.command
This script installs the certifi package and creates a symbolic link so Python knows where to find the bundle.
2. Updating Certifi and Environment VariablesSometimes your local certificate bundle is just too old to recognize newer Certificate Authorities (CAs). The certifi library provides a curated, 24/7-updated collection of Root Certificates. Start by pulling the latest version:
pip install --upgrade certifi
Now, force Python to use this specific bundle by setting environment variables in your shell profile (.bashrc or .zshrc):
export SSL_CERT_FILE=$(python -m certifi)
export REQUESTS_CA_BUNDLE=$(python -m certifi)
3. Handling Corporate Proxies (The Enterprise Way)In a corporate environment, your company likely intercepts SSL traffic to scan for threats. certifi won't work here because it doesn't know about your company’s private Root CA. You need to manually add your company's certificate to the trust store. First, export your corporate cert as a .pem file, then append it to the existing bundle:
# Find the path to your current certifi bundle
python -m certifi
# Example output: /usr/local/lib/python3.11/site-packages/certifi/cacert.pem
# Append your corporate cert to the end of the file
cat company_root.pem >> /path/to/your/certifi/cacert.pem
4. The "Temporary" (Unsafe) FixIf you are testing a local server with a self-signed certificate and security is not a priority, you can bypass verification. Never use this code in a production environment. It leaves you wide open to Man-in-the-Middle (MITM) attacks.
import requests
# For the requests library
response = requests.get('https://localhost:8000', verify=False)
# For global urllib context
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
Verification: Is it fixed?Run this quick check to see if Python can now establish a secure handshake with a major site:
import requests
try:
response = requests.get('https://google.com', timeout=5)
print(f"Success! Status code: {response.status_code}")
except Exception as e:
print(f"Connection failed: {e}")

