元数据
| id | pip-ssl-cert | 收录 | 2026-09-01 |
|---|---|---|---|
| 问题域 | 编码代理与终端 | 主题分组 | 网络与镜像 |
| 适用工具 | pip · 外部工具 | ||
症状
`pip install` 报 `SSLError(SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate)` 或 `unable to get local issuer certificate` / `certificate has expired`;浏览器能打开包源、curl 正常,唯独 pip 失败;换源后错误不变。
原因
HTTPS 的证书校验链路是「客户端信任库 → 服务器证书 → 逐级验签直到根证书」:pip 用自己捆绑的 CA 包(certifi),与浏览器/系统信任库是**三套独立列表**——这就是「浏览器能开、curl 能下、pip 报错」的机制解释。校验有三个独立条件:①链上每张证书都在信任库可追溯(自签证书在链顶端直接断裂 → `self-signed certificate`);②证书未过期且系统时间正确(→ `certificate has expired`);③主机名与证书 SAN 匹配。公司代理 MITM 时,代理对每个站点签发的都是「代理自己的证书」,所以**换源必然无效**——证书不是源的,是代理的。确定性判断路径:`openssl s_client -connect <host>:443 -showcerts` 看 `subject=`/`issuer=` 是否相同(自签)或 issuer 是否为内部 CA(MITM);`--trusted-host` 是把「校验」整段短路,不是修复,装包前想清楚这一点。
pip 的 HTTPS 证书链校验失败:服务器证书不是由 pip 信任库(certifi)里任何 CA 签发。三种来源:自签证书(内网镜像/私有仓库)、公司 SSL 拦截代理的私有 CA(换源无效)、证书过期/系统时间错。`--trusted-host` 是跳过校验不是修复。
解法
1. **正规修复**:CA 证书导入系统信任库(`/usr/local/share/ca-certificates/` + `update-ca-certificates`),再让 pip 指向系统 bundle(`pip config set global.cert /etc/ssl/certs/ca-certificates.crt`)——实测 SSL 阶段通过。
2. **临时绕过**:`pip install --trusted-host <域名> ...`,仅救急,长期等于关掉 HTTPS 身份校验。
3. **场景分流**:内网镜像找管理员要 CA;公司代理找 IT 要拦截证书(换源无用);`expired` 先校时。
4. **Python 3.14 注意**:源地址用域名不用 IP 字面量(实测会触发 `check_hostname requires server_hostname`)。
验证证据
- 2026-09-01 本机实测(WSL Ubuntu 26.04,Python 3.14.4,pip 25.1.1,自建 127.0.0.1:18443 HTTPS 源 + 自签 CA,SAN=DNS:localhost,IP:127.0.0.1):`实测复现日志(2026-09-01)`
- 基线:`pip install --index-url https://localhost:18443/simple/ six` → `[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1081)`(实锤);
- 快速绕过:`--trusted-host localhost` → SSL 阶段放行,后续报 `No matching distribution`(实锤:跳过校验生效);
- 正规修复:CA 复制进 `/usr/local/share/ca-certificates/` + `update-ca-certificates` + `pip --cert /etc/ssl/certs/ca-certificates.crt` → SSL 阶段放行(实锤);
- 负对照:撤销 CA + 不带 `--cert` → 回到 `CERTIFICATE_VERIFY_FAILED: self-signed certificate`(实锤:修复确实依赖信任库,不是缓存);
- 变体:`https://127.0.0.1:18443`(IP 字面量)→ `ValueError: check_hostname requires server_hostname`(Python 3.14 新行为,实锤)。
来源与证据
素材来源:本站实测沉淀(2026-09-01)
验证证据:Evidence details kept locally (2026-09-01)