Coding Agents & Terminal · recorded 2026-09-01 · applies to 1 tool(s) ✓ Verified

Problem: pip install fails with SSL certificate error (CERTIFICATE_VERIFY_FAILED) — the trust chain broke, don't rush to --trusted-host —— Trove problem library

Verified 2026-09-01pip reports CERTIFICATE_VERIFY_FAILED — root cause is the CA trust chain does not include that certificate (self-signed / private internal CA / corporate proxy MITM); --trusted-host only skips verification (verified: SSL stage passes); proper fix = import the CA into the system trust store + point pip at the system bundle with `pip --cert` (verified, with a working negative control).

Quick fix

# Option A (emergency, get installed now): skip cert verification for one host
pip install --trusted-host <mirror-host> --index-url <mirror-url> <package>
# Persist: pip config set global.trusted-host <mirror-host>

# Option B (proper fix, recommended): import the CA into the system trust store, point pip at the system bundle
sudo cp your-ca.crt /usr/local/share/ca-certificates/pipssl.crt
sudo update-ca-certificates
pip config set global.cert /etc/ssl/certs/ca-certificates.crt
# then a normal pip install works, no --trusted-host needed

What you will run into

pip install suddenly fails with a long error:

ERROR: Exception:
HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/xxx/
SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1081)'))

The confusing part: the browser opens PyPI fine, curl downloads fine, only pip reports a certificate error; or you switch to a domestic mirror (Tsinghua/Aliyun) and get the exact same error.

Common scenes: an internal PyPI mirror (Nexus/devpi), a corporate/campus SSL-interception proxy, a private repo with a self-signed cert, or an expired certificate.

Before you reflexively add --trusted-host, know that it skips verification — it takes the lock off HTTPS, and packages installed that way are not guaranteed to be what they claim to be.

Why this happens

When pip downloads over HTTPS, Python's ssl module runs certificate chain validation: the server certificate must be issued by a CA that pip trusts, every certificate in the chain must be present, unexpired, and the hostname must match.

When validation fails, pip fails rather than download — that is a security design, not a bug.

"A broken trust chain" usually means one of three things:

  1. Self-signed certificate: the server uses a certificate it issued to itself (common for internal mirrors and private repos) — it is not in any CA trust store → self-signed certificate / unable to get local issuer certificate;
  2. Corporate proxy MITM: the network uses an SSL-interception proxy that issues its own certificate for every site. The browser works because the proxy CA was installed there; pip uses its own separate trust store, so it fails — switching mirrors does not help, because the certificate is issued by the proxy, not by the mirror;
  3. Expired certificate / wrong system clock: you see certificate has expired.

What --trusted-host does is tell pip to skip verification for that host — it is not a fix, it is a bypass. Verified locally (see evidence): with --trusted-host the SSL stage passes straight through.

Fine as an emergency, but as a habit it means nobody verifies "is this really pypi.org".

Step by step

1. Confirm the error shape — Required

pip install --index-url https://<your-source>/simple/ some-package 2>&1 | tail -5
# Seeing CERTIFICATE_VERIFY_FAILED / self-signed / unable to get local issuer = this page

Then inspect the server certificate to tell "self-signed" from "private CA":

echo | openssl s_client -connect <mirror-host>:443 -showcerts 2>/dev/null | grep -E "subject=|issuer=" | head -4
# subject=CN=xxx equals issuer=CN=xxx = self-signed; issuer is an internal CA = MITM/private CA

2. Figure out which scenario — Required

  • Internal mirror / private repo → ask the admin for the CA (Option B);
  • Corporate/campus proxy interception → ask IT for the proxy CA; switching mirrors is useless (the cert is issued by the proxy, every host fails the same way);
  • certificate has expired → check the clock first (date), then think about expiry;
  • Browser fine, pip fails → the classic "pip trust store missing the CA" signature; Option B is the direct cure.

3. Proper fix: import the CA into the system trust store — Required

sudo cp ca.crt /usr/local/share/ca-certificates/pipssl.crt
sudo update-ca-certificates   # "1 added" in the output = success

4. Point pip at the system trust store — Required

pip config set global.cert /etc/ssl/certs/ca-certificates.crt
# or env var: export PIP_CERT=/etc/ssl/certs/ca-certificates.crt
# or one-shot: pip install --cert /etc/ssl/certs/ca-certificates.crt ...

5. The bypass is for emergencies only — Optional

pip install --trusted-host <mirror-host> --index-url <mirror-url> <package>

Only for "internal test env, need it now". Once installed, switch to Option B. And remember: packages installed while verification is off are not guaranteed to come from a trustworthy source.

How to confirm it works

# 1. Install without any --trusted-host / --cert (public source or yours)
pip install --index-url https://<your-source>/simple/ <package>
# 2. Positively verify the chain is trusted
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt your-cert.crt
# "OK" = the certificate is now accepted by the system trust store

Pass criteria: no bypass flag, no SSL error, install succeeds — all three at once.

Other environments

EnvironmentBehavior & fix
Python 3.14 + IP-literal source (https://127.0.0.1:port)ValueError: check_hostname requires server_hostname (verified 2026-09-01): 3.14 sends no SNI for IP literals, ssl hard-fails — use a hostname (localhost or internal DNS), not an IP
WindowsImport CA: certutil -addstore Root ca.crt (Trusted Root Certification Authorities); point pip: pip config set global.cert C:\path\ca-bundle.crt
macOSImport into Keychain (System) and trust it; pip: same --cert pointing at the exported bundle
Docker containerContainers don't inherit host CAs: COPY ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates at build time; pip config at runtime too
Switching mirrors still failsLikely proxy MITM: the cert is issued by the proxy, changing hosts does nothing — install the proxy CA (step 2)
Wrong system clockcertificate has expired: fix the time first (timedatectl / ntp), then deal with the cert

Symptom

pip install reports SSLError(SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate) or unable to get local issuer certificate / certificate has expired; the browser opens the package source and curl works, only pip fails; the error stays identical after switching mirrors.

Cause

pip's HTTPS certificate chain validation fails: the server certificate is not issued by any CA in pip's trust store (certifi). Three origins: self-signed (internal mirror / private repo), a corporate SSL-interception proxy's private CA (mirror switching is useless), or an expired certificate / wrong clock. --trusted-host skips verification; it does not fix anything.

Solution

  1. Proper fix: import the CA into the system trust store (/usr/local/share/ca-certificates/ + update-ca-certificates), then point pip at the system bundle (pip config set global.cert /etc/ssl/certs/ca-certificates.crt) — verified: SSL stage passes.
  2. Emergency bypass: pip install --trusted-host <host> ... — rescue only; as a habit it disables HTTPS identity verification.
  3. Scenario routing: internal mirror → ask admin for CA; corporate proxy → ask IT for the interception cert (mirror switching useless); expired → fix the clock first.
  4. Python 3.14 note: use a hostname, not an IP literal, for the source URL (verified: triggers check_hostname requires server_hostname).

Verification evidence

  • 2026-09-01 local test (WSL Ubuntu 26.04, Python 3.14.4, pip 25.1.1; self-hosted HTTPS source at 127.0.0.1:18443 with a self-signed CA, SAN=DNS:localhost,IP:127.0.0.1): local repro log 2026-09-01
  • Baseline: pip install --index-url https://localhost:18443/simple/ six → [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1081) (reproduced);
  • Bypass: --trusted-host localhost → SSL stage passes, then No matching distribution (verified: skip works);
  • Proper fix: CA copied to /usr/local/share/ca-certificates/ + update-ca-certificates + pip --cert /etc/ssl/certs/ca-certificates.crt → SSL stage passes (verified);
  • Negative control: CA removed, no --cert → back to CERTIFICATE_VERIFY_FAILED: self-signed certificate (verified: the fix really depends on the trust store, not a cache);
  • Variant: https://127.0.0.1:18443 (IP literal) → ValueError: check_hostname requires server_hostname (Python 3.14 new behavior, verified).

Related canonicals

Applicable tools (1)

This problem is linked from the tool pages below — reachable from either side.

pip

Source & evidence

Recorded Verified ✓ VerifiedApplies to 1 tool(s)
Entries contain locally measured or reviewable-source solutions; unmeasured items are explicitly labeled.

← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk