# pip SSL: CERTIFICATE_VERIFY_FAILED：不用 --trusted-host 的修法

> pip 报 CERTIFICATE_VERIFY_FAILED 表示无法验证 PyPI 或镜像源的证书。应升级 pip、用 --cert 或 PIP_CERT 指定证书包，避免 --trusted-host。

Source: https://ssl.sh/zh/errors/pip-ssl-certificate-verify-failed/
Last updated: 2026-11-10

**`There was a problem confirming the ssl certificate ... CERTIFICATE_VERIFY_FAILED` 的意思是：pip 连上了 PyPI 或你配置的软件源，但无法验证它的证书。** 在公司网络里，几乎都是 HTTPS 解密代理的根证书 pip 不认识；其他情况是 pip 太旧、私有仓库用了内部 CA，或者镜像源配置有问题。

## 快速修复

最快的修法是把 pip 升级到 24.2 以上，让它读取系统证书库；不能升级就用 `--cert` 指定证书包。先看 pip 版本，因为 pip 24.2 及以后会同时信任操作系统证书库：

```bash
python -m pip --version
python -m pip install --upgrade pip --cert /path/to/ca-bundle.pem
```

如果你有包含代理根证书或内网根证书的证书包，把它写进配置长期生效：

```bash
pip config set global.cert /path/to/ca-bundle.pem
# 或者只在当前 shell / CI 任务中生效
export PIP_CERT=/path/to/ca-bundle.pem
```

`ca-bundle.pem` 应该是系统证书包（如 `/etc/ssl/certs/ca-certificates.crt`）再追加你的根证书（PEM 格式），因为 `--cert` 会替换默认列表，而不是在它基础上追加。

## 原因

办公电脑上最常见的是公司的 HTTPS 解密代理，其次是私有仓库的内部 CA 和过旧的 pip。

### 1. 公司的 HTTPS 解密代理

办公电脑上的 pip 证书报错，大多来自公司上网行为管理或安全网关用公司根证书重新签名 HTTPS，见[企业 SSL 解密](https://ssl.sh/zh/errors/corporate-ssl-inspection/)。

- **确认：** `openssl s_client -connect pypi.org:443 -servername pypi.org </dev/null | grep issuer` 显示的是公司名或安全厂商，而不是公开 CA。
- **修复：** 在 IT 已把根证书装进系统证书库的电脑上，升级到 pip 24.2 以上；或者用 `--cert` / `PIP_CERT` 指定根证书。

### 2. 私有仓库使用内部 CA

Nexus、Artifactory、devpi 等私有 Python 仓库，证书常由公司内部 CA 签发。

- **确认：** 报错里的主机名是内网地址，而不是 pypi.org。
- **修复：** 把内部根证书加到 `--cert` 使用的证书包里，或者装进系统证书库并使用能读取它的 pip 版本。

### 3. pip 或 Python 太旧

老版本 pip 自带的 certifi 也很旧，缺少较新的根证书。

- **确认：** `pip --version` 显示的是好几年前的版本，报错的站点使用较新的根证书。
- **修复：** 升级 pip（必要时加 `--cert`）。已停止维护的 Python 版本，直接升级 Python。

### 4. 镜像源证书链有问题

镜像站没发中间证书时，浏览器能打开，pip 却会失败。

- **确认：** 把镜像域名输入 [SSL 检测工具](https://ssl.sh/zh/tools/ssl-checker/)检查。
- **修复：** 换一个配置正确的镜像源，或者反馈给镜像维护方。清华 TUNA、阿里云、腾讯云、华为云的 PyPI 镜像都提供 HTTPS，配置时直接用 `https://` 地址：

```bash
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
```

### 5. 代理地址写错

如果报错是 `WRONG_VERSION_NUMBER` 或 `EOF occurred in violation of protocol`，而不是证书错误，问题出在代理配置，见 [WRONG_VERSION_NUMBER](https://ssl.sh/zh/errors/python-ssl-wrong-version-number/)。

## 各环境修法

根证书装进系统证书库后，pip 24.2 以上会直接读取；旧版本和容器里用 `PIP_CERT` 或 `--cert` 指定证书包。

**Windows：** pip 24.2 以上会自动信任域策略下发的根证书，旧版本需要 `--cert`。用户配置文件在 `%APPDATA%\pip\pip.ini`。

**macOS：** pip 24.2 以上通过 truststore 读取钥匙串，公司根证书要在“系统”钥匙串里并设为信任。

**Linux：** 用 `update-ca-certificates`（Debian/Ubuntu）或 `update-ca-trust`（CentOS/RHEL 系）安装根证书，然后依靠 pip 24.2 以上的系统证书支持，或者把 `PIP_CERT` 指向系统证书包。

**Docker / CI：**

```dockerfile
COPY company-root.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates
ENV PIP_CERT=/etc/ssl/certs/ca-certificates.crt
RUN python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
```

**其他安装工具：** conda 有自己的 `ssl_verify` 设置，可以填 CA 证书包路径；uv 可以用 `--native-tls` 使用系统证书库，或用 `SSL_CERT_FILE` 指定证书包。

## 不要这样做

这 4 种做法要么让安装的包失去来源验证，要么在下次升级时失效。

- **`--trusted-host pypi.org --trusted-host files.pythonhosted.org`。** 这是转载最多的答案，它关闭的是这些主机的证书校验，而软件包是你随后要执行的代码。
- **把镜像地址改成 `http://` 再配合 `--trusted-host`。** 国内不少旧教程这样写，结果是每个下载在网络上都能被读取和替换。
- **`--use-deprecated=legacy-certs`** 想“回到以前的样子”。它去掉了系统证书库支持，通常会让代理问题更严重。
- **直接修改 site-packages 里 certifi 的 `cacert.pem`。** 下次 `pip install --upgrade certifi` 会悄悄覆盖它，应该维护一份自己的证书包文件。
