aiohttp 默认严格校验ssl证书,遇自签名/过期/域名不匹配证书会抛出ssl.sslcertverificationerror;开发时应传入ssl=false的tcpconnector,而非简单设ssl=false参数。

为什么 aiohttp 会报 ssl.SSLCertVerificationError
当你用 aiohttp.ClientSession() 发起 HTTPS 请求时,如果目标服务器用了自签名证书、过期证书、或域名不匹配的证书,aiohttp 默认会严格校验 SSL 证书并抛出 ssl.SSLCertVerificationError。这不是 bug,是安全默认行为 —— 它和 requests 的 verify=False 类似,但 aiohttp 不提供直接的布尔开关。
如何让 aiohttp 跳过 SSL 验证(开发/测试场景)
必须显式传入一个不验证证书的 aiohttp.TCPConnector 实例,不能只改 session 参数。常见错误是以为加个 ssl=False 就行,其实那只是关闭 SSL 协议本身(HTTP → HTTP),不是跳过验证。
- 正确做法:创建
connector = aiohttp.TCPConnector(ssl=False),再传给ClientSession(connector=connector) - 若你用的是 Python 3.11+ 且 aiohttp ≥ 3.9,
ssl=False确实等价于跳过验证;旧版本需用ssl=context方式(见下条) - 更细粒度控制(如仅忽略证书链但保留主机名检查)可传入自定义
ssl.SSLContext,但绝大多数调试场景用ssl=False足够
使用自定义 SSLContext 绕过验证(兼容旧版本或需要微调)
某些老项目用的是 aiohttp ssl=False 可能触发 DeprecationWarning 或不生效。这时要手动构造一个不验证的上下文:
import ssl
import aiohttp
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
connector = aiohttp.TCPConnector(ssl=context)
async with aiohttp.ClientSession(connector=connector) as session:
async with session.get("https://self-signed.example") as resp:
print(await resp.text())
注意:context.check_hostname = False 和 context.verify_mode = ssl.CERT_NONE 缺一不可 —— 前者关主机名匹配,后者关证书链验证。只设一个仍可能失败。
生产环境绝对不要禁用 SSL 验证
禁用验证会让中间人攻击(MITM)完全失效,所有传输数据明文可见。哪怕内网服务,也应优先用私有 CA 签发证书并把根证书加入系统信任库,而不是在代码里写死 ssl=False。如果必须对接不可控的自签名服务,至少把证书文件路径传给 TCPConnector(ssl=ssl.create_default_context(cafile="path/to/cert.pem")),而不是彻底关闭验证。
最容易被忽略的一点:禁用 SSL 后,aiohttp 不再校验证书有效期和域名,但 DNS 解析、TCP 连接、HTTP 状态码这些仍照常工作 —— 错误不会消失,只是换了一种形式出现(比如 502、连接重置),别误以为“不报 SSL 错了就等于请求通了”。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











