# InsecureRequestWarning: Unverified HTTPS request 怎么消除

> urllib3 发出 InsecureRequestWarning，说明有 HTTPS 请求跳过了证书校验。找到 verify=False，修好它掩盖的证书问题，再把它删掉。

Source: https://ssl.sh/zh/errors/urllib3-insecure-request-warning/
Last updated: 2026-11-10

**`InsecureRequestWarning: Unverified HTTPS request is being made to host 'example.com'` 的意思是：程序发出了一个没有校验服务器证书的 HTTPS 请求。** 请求本身成功了。之所以警告，是因为某段代码（通常是为了绕过证书报错而加的 `verify=False`）关掉了“确认对方是真服务器”这道检查。正确的处理方式是找到这个开关，解决它掩盖的证书问题，然后把它删掉。

## 这条警告是什么意思

这条警告表示 urllib3 建立了一条没有校验证书的 HTTPS 连接。urllib3 是 requests、pip、botocore 和大量 SDK 底层的 HTTP 库。它在发送每个请求之前都会检查连接：如果连接是用 `cert_reqs='CERT_NONE'` 建立的（requests 里的 `verify=False` 最终就变成这个），又没有固定证书指纹，就会调用 `warnings.warn()` 输出：

```text
InsecureRequestWarning: Unverified HTTPS request is being made to host 'example.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
```

Python 会在前面加上触发警告的文件和行号，比如 `.../urllib3/connectionpool.py:<行号>:`。这个位置在 urllib3 内部而不是你的代码里，所以很多人找不到源头。流量仍然是加密的，但没有校验时，你是在和“任何应答的人”加密通信。代理、被劫持的 DNS、恶意 Wi-Fi 都可以出示任意证书并读取数据。

还要分清它和报错：`CERTIFICATE_VERIFY_FAILED` 是校验失败、请求被拦下；`InsecureRequestWarning` 是没有校验、请求被放行。前者说明保护在起作用，后者说明保护已经被拆掉了。所以出现警告时，不要把“消除提示”当成目标，目标应该是“让校验重新开启并且通过”。

## 常见原因（按出现频率排序）

这条警告有 4 种来源，最常见的是代码里为绕过证书报错写了 `verify=False`。

### 1. 代码里写了 verify=False

典型经过：请求报 [CERTIFICATE_VERIFY_FAILED](https://ssl.sh/zh/errors/python-certificate-verify-failed/)，有人从网上抄来 `verify=False`，报错变成了这条警告。

- **确认：** 在代码库里搜索：

```bash
grep -rnE "verify\s*=\s*False|CERT_NONE|_create_unverified_context" --include=*.py .
```

- **修复：** 删掉它，再按下文修复原本的证书错误。

### 2. Session、客户端或 SDK 的选项关闭了校验

`session.verify = False`、某个客户端的 `ssl_verify=False` 参数、AWS CLI 的 `--no-verify-ssl` 这类命令行开关，最终都会变成一条未校验的 urllib3 连接。它们经常藏在配置文件或“开发环境”配置里。

- **确认：** 把警告变成异常，拿到完整调用栈：

```bash
python -W error::urllib3.exceptions.InsecureRequestWarning app.py
```

- **修复：** 把选项改回开启，并通过这个库自己的参数指定 CA 证书包。

### 3. 第三方库自己关闭了校验

一些老旧 SDK、爬虫框架会把 `verify=False` 写死在代码里，上一步的调用栈会停在这个包里面。

- **修复：** 升级这个包，或者给它传入开启校验的 session、参数。都不支持的话就向维护者报告，这属于库的安全漏洞。

### 4. 直接用 urllib3 并设置了 CERT_NONE

`urllib3.PoolManager(cert_reqs="CERT_NONE")` 会让每个请求都发出警告。删掉这个参数即可，urllib3 2.x 默认就会校验。

## 分平台修复命令

每种做法都是让 Python 能信任服务器，之后就可以删掉 `verify=False`。先用 [SSL 检测工具](https://ssl.sh/zh/tools/ssl-checker/)检查服务器，如果证书链不完整，应该修服务器而不是客户端。

**Debian / Ubuntu / 容器：**

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

**CentOS / RHEL / 阿里云 Alibaba Cloud Linux：**

```bash
cp company-root.crt /etc/pki/ca-trust/source/anchors/ && update-ca-trust
export REQUESTS_CA_BUNDLE=/etc/pki/tls/certs/ca-bundle.crt
```

**Windows 和 macOS：** 用 truststore（Python 3.10+）读取系统证书库，公司 IT 下发的根证书也能识别：

```python
import truststore
truststore.inject_into_ssl()
```

**只访问某一个内网服务：**

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

**无法更换证书的自签名设备（路由器、NAS、摄像头管理页等）：** 用 `openssl s_client -connect 203.0.113.10:443 -showcerts` 导出证书，在[证书解析工具](https://ssl.sh/zh/tools/certificate-decoder/)里核对内容，然后作为 `verify=` 传入，或者用 urllib3 固定指纹：

```python
import urllib3
http = urllib3.PoolManager(
    cert_reqs="CERT_NONE",
    assert_fingerprint="<设备证书的 SHA-256 指纹>",
)
http.request("GET", "https://203.0.113.10/")
```

设置了 `assert_fingerprint` 后，urllib3 会把连接视为已校验，不再输出警告；对方一旦换了证书就会被拒绝。

**国内装包：** 安装 truststore 时可以用清华镜像 `pip install -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple truststore`。镜像地址用 `https://`，不要加 `--trusted-host`。

## 不推荐：把警告屏蔽掉

`urllib3.disable_warnings()`、`requests.packages.urllib3.disable_warnings()`、`warnings.filterwarnings("ignore")`、`PYTHONWARNINGS=ignore`，都只是让提示消失，风险一点没少。urllib3 文档明确写着强烈不建议发出未校验的 HTTPS 请求。没有校验时，处在你和服务器之间的任何人都能读取或篡改账号密码、会话 Cookie 和接口数据；屏蔽警告还会让这个漏洞在日志和代码审查里看不出来。

正当场景都有替代办法：内网和代理根证书用 CA 证书包，系统里已有的根证书用 truststore，缺中间证书就补全服务器证书链，确实无法更换证书的单台设备用证书固定。证书链为什么重要，见[证书链原理](https://ssl.sh/zh/learn/certificate-chains/)。

## 团队里怎么防止它再出现

防止复发要靠流程：在代码审查清单里列出 `verify=False`、`CERT_NONE`、`disable_warnings` 这几个关键词，提交时自动搜索；测试和预发环境也使用由内部测试 CA 签发的证书，而不是自签名证书加关闭校验，这样测试走的就是和生产一样的校验路径。需要访问公司内网服务的脚本，统一在基础镜像里装好内网根证书并设置 `REQUESTS_CA_BUNDLE`，大家就没有理由再去关闭校验。

## 怎么确认已经修好

修好的标准是：把警告当成错误运行，脚本也不再失败。

```bash
python -W error::urllib3.exceptions.InsecureRequestWarning -c "import requests; print(requests.get('https://example.com', timeout=10).status_code)"
```

把警告当成错误运行，只要这条代码路径上还有未校验的请求，命令就会失败。最好在 CI 里也这样运行测试，以后有人再加 `verify=False`，构建会直接失败，而不是悄悄上线。最后再搜一遍代码里的 `verify=False` 和 `CERT_NONE`，确认已经清理干净。
