命令行工具

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

作者: · 最后核实

报错信息Could not fetch URL https://pypi.org/simple/requests/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/requests/ (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate')))

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 及以后会同时信任操作系统证书库:

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

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

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 解密。

  • 确认: 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 检测工具检查。
  • 修复: 换一个配置正确的镜像源,或者反馈给镜像维护方。清华 TUNA、阿里云、腾讯云、华为云的 PyPI 镜像都提供 HTTPS,配置时直接用 https:// 地址:
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。

各环境修法

根证书装进系统证书库后,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:

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 会悄悄覆盖它,应该维护一份自己的证书包文件。

常见问题

为什么浏览器能打开 pypi.org,pip 却报错?

浏览器信任操作系统证书库里的根证书,包括 IT 部门下发的公司代理根证书;pip 24.2 之前只信任自带的 certifi 列表,里面只有公开根证书。升级 pip,或者用 --cert 指定包含公司根证书的证书包。

pip 自己就报错,没法升级 pip 怎么办?

升级时临时指定一次证书包:python -m pip install --upgrade pip --cert /path/to/ca-bundle.pem。升级到 24.2 以上后,pip 会同时读取系统证书库。手头没有证书包文件的话,从系统证书库导出代理根证书(PEM 格式),追加到 certifi 证书文件的副本里。

很多教程让加 --trusted-host,安全吗?

不安全。pip 的说明是:把主机标记为可信,即使它没有有效的 HTTPS 甚至没有 HTTPS。之后 pip 会安装这个主机(或任何冒充它的人)返回的任何东西。只适合可信内网里的明文 HTTP 私有源,绝不要用于 PyPI 或公开镜像。

证书和镜像源配置写在哪里最省事?

执行 pip config set global.cert /path/to/ca-bundle.pem,会写进用户配置文件(Linux、macOS 是 pip.conf,Windows 是 pip.ini)。镜像源同理:pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple。pip config debug 可以列出 pip 读取的所有配置文件和各项值的来源。

资料来源

  1. pip: HTTPS Certificates
  2. Python docs: ssl — TLS/SSL wrapper for socket objects
  3. curl: CA certificates extracted from Mozilla