# CERTIFICATE_VERIFY_FAILED：Python 证书验证失败的原因与修法

> Python 报 [SSL: CERTIFICATE_VERIFY_FAILED] 表示无法验证服务器证书。应补全服务器证书链或给 Python 指定正确的 CA 证书包，而不是 verify=False。

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

**`[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed` 的意思是：Python 连上了服务器，但无法用自己信任的 CA 证书验证服务器证书。** 绝大多数情况要么是服务器没发中间证书，要么是当前 Python 环境用的 CA 证书包是空的、过旧的或者不对。

## 快速修复

服务器证书链不全就修服务器，链完整就修 Python 的 CA 证书包。先在命令行测试服务器：

```bash
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
```

如果只返回一张证书，结果是 `Verify return code: 21` 或 `20`，说明服务器证书链不完整，要去服务器上修（见[缺少中间证书](https://ssl.sh/zh/errors/missing-intermediate-certificate/)）。如果证书链完整，就修客户端：

```bash
# 查看 Python 的 OpenSSL 去哪里找 CA 证书
python3 -c "import ssl; print(ssl.get_default_verify_paths())"

# requests：指定包含所需根证书的证书包
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt

# urllib / ssl：让 OpenSSL 使用这个证书包
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
```

macOS 上用 python.org 安装包装的 Python，每个版本都要运行一次 `/Applications/Python 3.x/Install Certificates.command`。

## 原因

这个报错有 6 种原因，访问公网网站时最常见的是服务器证书链不完整。

### 1. 服务器证书链不完整

OpenSSL 需要服务器把中间证书一起发过来，它不会自己去下载。

- **确认：** `openssl s_client -showcerts` 只显示 `depth=0`，或者 [SSL 检测工具](https://ssl.sh/zh/tools/ssl-checker/)提示证书链有问题。
- **修复：** 服务器配置完整证书链（Nginx 用 `fullchain.pem`，宝塔里“证书”一栏要粘贴含中间证书的内容）。只在客户端打补丁不能根治。

### 2. Python 找不到任何 CA 证书

python.org 的 macOS 安装包自带一份 OpenSSL，但在运行 Install Certificates.command 之前没有 CA 证书包。自己编译的 Python、精简容器镜像和一些嵌入式发行版也会这样。

- **确认：** `ssl.get_default_verify_paths()` 返回 `cafile=None`，`capath` 为空或目录不存在。
- **修复：** 运行 Install Certificates.command，或安装系统的 `ca-certificates` 包，或用 `SSL_CERT_FILE` 指向一个有效的证书包。

### 3. requests 看不到企业根证书或内网根证书

requests 只用 certifi，里面只有 Mozilla 收录的公开根证书。IT 部门通过域策略装进 Windows 的根证书，requests 看不到。

- **确认：** 同一个地址 urllib 正常、requests 失败，或者服务器证书的签发者是公司内部 CA。
- **修复：** 显式指定证书包：

```python
import requests
requests.get("https://intranet.example.com", verify="/etc/ssl/certs/company-bundle.pem")
```

也可以安装 `truststore`（Python 3.10 及以上），在程序启动时调用 `truststore.inject_into_ssl()`，让 Python 直接使用操作系统的信任库。

### 4. 企业代理、上网行为管理或杀毒软件解密 HTTPS

这类设备和软件会用自己的根证书重新签名流量，`openssl s_client` 输出中的 `issuer` 会显示代理厂商或公司名称。

- **修复：** 向 IT 部门要代理根证书（PEM 格式），追加到证书包副本里，再用 `REQUESTS_CA_BUNDLE` / `SSL_CERT_FILE` 指向这个副本。

### 5. Python 3.13 及以上的严格校验

报错结尾是 `Missing Authority Key Identifier` 或提到其他证书扩展时，说明证书本身不符合 RFC 5280，被严格模式拒绝。按规范重新签发内部 CA 和站点证书。

### 6. 证书过期或系统时间不对

`certificate has expired` 说明证书链是可信的，只是日期不对。续期证书，或者校正客户端时间。

## 各环境修法

各环境的修法都是让 Python 用上一份包含所需根证书的 CA 证书包，区别在证书包的位置和更新方式。

**macOS（python.org 安装包）：** 运行 Install Certificates.command，它会安装 certifi 并把它链接为该 Python 的 OpenSSL 证书包。

**macOS（Homebrew 安装的 Python）：** 使用 Homebrew 的 OpenSSL 和证书包，`brew upgrade` 即可更新；私有根证书用 `SSL_CERT_FILE` 追加。

**Windows：** `ssl.create_default_context()` 会加载 Windows 的 ROOT 和 CA 证书库，所以 urllib 通常信任公司下发的根证书；requests 不会，需要 `verify=`、`REQUESTS_CA_BUNDLE` 或 truststore。

**Debian / Ubuntu / Docker：**

```bash
apt-get update && apt-get install -y ca-certificates
cp company-root.crt /usr/local/share/ca-certificates/ && update-ca-certificates
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
```

国内服务器 `apt-get update` 慢时，可以先把软件源换成阿里云、腾讯云或清华镜像，但 `ca-certificates` 必须安装。

**CentOS / RHEL / 阿里云 Alibaba Cloud Linux：** 根证书放进 `/etc/pki/ca-trust/source/anchors/`，执行 `update-ca-trust`，证书包路径是 `/etc/pki/tls/certs/ca-bundle.crt`。CentOS 7 这类老系统的证书包往往很久没更新，先 `yum update ca-certificates`。

**pip 本身**有单独的参数，见 [pip SSL 证书验证失败](https://ssl.sh/zh/errors/pip-ssl-certificate-verify-failed/)。

## 不要这样做

关闭校验后报错会消失，Python 也不再验证服务器身份。

- **`requests.get(url, verify=False)`。** 证书和域名校验全部关闭，链路上任何人都能冒充服务器，读到你的 token 和密码。
- **`ssl._create_default_https_context = ssl._create_unverified_context`。** 这段网上流传很广的代码会让整个进程都不校验证书，连你没写过的第三方库也一起受影响。
- **用 `urllib3.disable_warnings()` 屏蔽 InsecureRequestWarning。** 警告没了，漏洞还在，以后更难排查。
- **为了让不规范的内部证书通过而全局关闭 `VERIFY_X509_STRICT`。** 应该修证书，而不是放宽校验。
