Introduction
pip uses TLS to securely download packages from PyPI, but SSL certificate verification fails when the system's CA certificate bundle is outdated, when behind a corporate proxy that performs TLS interception, or when the certifi package is not installed. The error SSL: CERTIFICATE_VERIFY_FAILED prevents any package installation from PyPI, effectively breaking the development environment. While the --trusted-host flag bypasses verification, it exposes the system to man-in-the-middle attacks and should only be used as a last resort with full understanding of the risks.
Symptoms
WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1007)'))': /simple/requests/Or:
Could not fetch URL https://pypi.org/simple/requests/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/requests/ (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED]')))Common Causes
- Outdated CA certificates: System CA bundle does not include recent root certificates
- Corporate proxy with TLS inspection: Proxy replaces certificates with corporate CA
- certifi package missing: Python's certifi CA bundle not installed
- Python compiled without SSL support: Custom Python build missing OpenSSL
- System clock incorrect: Certificate appears expired due to wrong system time
- Network firewall blocking: Firewall intercepts HTTPS traffic
Step-by-Step Fix
Step 1: Update certifi and verify certificates
```bash # Install/update certifi pip install --upgrade certifi
# Find certifi's CA bundle path python -c "import certifi; print(certifi.where())" # Output: /usr/local/lib/python3.11/site-packages/certifi/cacert.pem
# Verify the certificate chain python -c " import ssl import socket
context = ssl.create_default_context() with socket.create_connection(('pypi.org', 443)) as sock: with context.wrap_socket(sock, server_hostname='pypi.org') as ssock: print('SSL verified:', ssock.version()) print('Certificate:', ssock.getpeercert()) " ```
Step 2: Configure corporate proxy with custom CA
```bash # Set the CA bundle for pip export PIP_CERT=/path/to/corporate-ca-bundle.crt export REQUESTS_CA_BUNDLE=/path/to/corporate-ca-bundle.crt
# Or configure in pip.conf # ~/.pip/pip.conf (Linux) or %APPDATA%\pip\pip.ini (Windows) [global] cert = /path/to/corporate-ca-bundle.crt proxy = http://proxy.company.com:8080
# Install packages pip install requests ```
Step 3: Use trusted-host as last resort
```bash # ONLY use when no other option is available # This disables SSL verification and is vulnerable to MITM attacks pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org requests
# Safer: Use internal PyPI mirror with proper certificates pip install --index-url https://internal-pypi.company.com/simple/ \ --cert /path/to/corporate-ca-bundle.crt \ requests ```
Prevention
- Keep certifi package updated with
pip install --upgrade certifi - Configure PIP_CERT environment variable in shell profile for corporate environments
- Use a private PyPI mirror (devpi, Artifactory) with proper certificate management
- Add certificate verification tests to CI/CD pipelines
- Document corporate proxy configuration in team onboarding guides
- Never use --trusted-host in production deployment scripts
- Use pip.conf/requirements.txt with explicit certificate paths for reproducible builds