SSLCertVerificationError on pypi.org: Which CA Bundle Does pip Actually Trust?
0 reputation · 09 Feb 2021, 18:55 UTC
0 reputation · 09 Feb 2021, 18:55 UTC
When pip connects to pypi.org over HTTPS, it validates the server's TLS certificate against a trusted CA bundle. If that chain cannot be established, the client aborts with SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate. The failure is well documented, yet the exact trust store pip selects in a given environment is often ambiguous.
On systems where OpenSSL, the certifi package, and environment variables such as SSL_CERT_FILE or SSL_CERT_DIR are all present, it is unclear which source pip treats as authoritative. A virtual environment may bundle its own certifi wheel while the host OpenSSL library points to a different system store, and an override variable can silently redirect validation to an unexpected file. The resulting behavior depends on the Python build, the pip version, and the platform's certificate layout, making it difficult to predict which bundle is active without inspecting the runtime.
Which CA source does pip prioritize when multiple are available, and how can an operator determine the effective trust store without modifying the installation? Does the presence of a bundled certifi always shadow the system OpenSSL defaults, or do environment variables override both? What is the expected precedence order across CPython, pip, and OpenSSL when validating certificates for PyPI?
35275 reputation · 09 Feb 2021, 22:10 UTC
pip itself does not perform TLS verification. It delegates HTTPS to the requests / urllib3 stack, which in turn uses Python’s ssl module. The effective trust store is therefore determined by the following precedence (from highest to lowest):
REQUESTS_CA_BUNDLE environment variable – if set, requests loads this file and ignores all other CA sources.certifi CA file – requests defaults to certifi.where() when the certifi package is importable.SSL_CERT_FILE / SSL_CERT_DIR environment variables – these influence Python’s ssl.get_default_verify_paths() and are honored by the ssl module when no certifi bundle is used.In practice, if certifi is installed in the interpreter that runs pip, it will shadow the system OpenSSL defaults for all code that goes through requests. However, environment variables can still override this, and code that uses ssl directly (outside requests) will fall back to the OpenSSL defaults unless overridden by SSL_CERT_FILE or SSL_CERT_DIR.
Confirm the interpreter pip is using. Run:
pip --version
# Example output: pip 24.0 from /usr/local/lib/python3.12/site-packages/pip (python 3.12)
This tells you the exact Python executable and its library path.Inspect the interpreter’s SSL defaults and certifi location. Execute the following one‑liner with the same Python binary that pip uses (replace /usr/local/bin/python3.12 with the path from the previous step):
/usr/local/bin/python3.12 - <<'PY'
import ssl, certifi, os
print('certifi bundle:', certifi.where())
print('ssl default verify paths:', ssl.get_default_verify_paths())
print('env overrides:', {k: v for k, v in os.environ.items() if k in ('SSL_CERT_FILE', 'SSL_CERT_DIR', 'REQUESTS_CA_BUNDLE')})
PY
The output shows which CA file certifi points to, the system OpenSSL paths, and any environment variables that might override them.Use pip’s verbose network logging to see the file actually used. Run a pip command with maximum verbosity:
pip install --verbose some‑package
# Look for a line similar to:
# Using CA file: /path/to/ca-bundle.crt
The path logged is the one that requests passed to ssl for verification.Cross‑check the three sources. If the CA file logged by pip matches the path printed by certifi.where(), certifi is in effect. If it matches one of the paths from ssl.get_default_verify_paths(), the system OpenSSL store is being used. If it matches an environment variable you set (e.g., SSL_CERT_FILE or REQUESTS_CA_BUNDLE), that variable overrides the others.
If you observe a mismatch between the CA file reported by pip and the paths printed in step 2, it may indicate that pip is being invoked by a different Python interpreter than the one you inspected. In that case, provide the output of pip --version and the environment variables present when you run pip.
pip’s trust store is a combination of requests’ default to certifi, any REQUESTS_CA_BUNDLE override, and finally the system OpenSSL defaults. The simplest way to determine which one is active is to inspect the interpreter’s SSL defaults, the certifi location, and the environment variables, and then confirm with pip’s verbose logging.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.