Python 3.6之前ssl模块不支持TLS 1.2+,导致SSLContext相关异常;应升级至Python 3.7+,若无法升级需手动配置SSLContext并同步关闭check_hostname和verify_mode,且仅限测试使用。

requests HTTPS请求报错 ssl.SSLContext 相关异常?先看是不是Python版本太低
Python 3.6 之前(尤其是 3.5 及更早)的 ssl 模块默认不支持 TLS 1.2+,而现代 HTTPS 站点普遍强制要求 TLS 1.2 或更高。此时 requests 底层调用 ssl.create_default_context() 会失败,抛出类似 AttributeError: module 'ssl' has no attribute 'SSLContext' 或 ValueError: check_hostname requires server_hostname 的错误。
这不是 requests 的 bug,而是底层 Python SSL 支持缺失。最直接的解法是升级 Python —— 建议至少使用 Python 3.7+,它自带完整 TLS 1.2 支持和稳定的 SSLContext 接口。
- 确认当前版本:
python --version - 若为 3.5 或更低,升级到 3.8+ 是首选方案(避免打补丁、绕过验证等危险操作)
- 若无法升级(如嵌入式环境),需手动构造兼容的
SSLContext,但仅限临时调试,不可用于生产
绕过 SSL 验证时为什么 verify=False 还报 check_hostname 错?
verify=False 只禁用证书链校验,但不关闭主机名验证(check_hostname=True 默认开启)。当 requests 使用自建 SSLContext 且未显式设置 check_hostname=False 时,即使证书无效,也会在握手阶段因主机名不匹配而中断。
正确做法不是只设 verify=False,而是确保底层上下文也关闭主机名检查:
立即学习“Python免费学习笔记(深入)”;
import ssl
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context
<p>class CustomHTTPAdapter(HTTPAdapter):
def init_poolmanager(self, *args, *<em>kwargs):
context = create_urllib3_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
kwargs['ssl_context'] = context
return super().init_poolmanager(</em>args, **kwargs)</p><p>session = requests.Session()
session.mount('https://', CustomHTTPAdapter())
response = session.get('<a href="https://www.php.cn/link/b05edd78c294dcf6d960190bf5bde635">https://www.php.cn/link/b05edd78c294dcf6d960190bf5bde635</a>', verify=False) # 此时才真正跳过验证
- 仅写
requests.get(url, verify=False)在某些旧 urllib3 版本下仍可能触发check_hostname报错 - 必须同步修改底层
SSLContext的check_hostname和verify_mode - 此方式仅用于测试或内网可信服务,公网请求禁用验证等于裸奔
自定义 CA 证书路径不生效?检查 REQUESTS_CA_BUNDLE 和 CERT_PATH 优先级
当你把企业根证书或私有 CA 放进文件,然后设置 verify='/path/to/cacert.pem' 却仍报证书不受信,大概率是证书路径没被真正加载 —— requests 实际使用的证书路径由多个来源决定,且有明确优先级:
- 函数参数
verify='/path/to/cert'最高优先级(但路径必须绝对且可读) - 环境变量
REQUESTS_CA_BUNDLE次之(例如export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-bundle.crt) - 系统默认路径(如 macOS 的
/etc/ssl/cert.pem,Linux 的/etc/pki/tls/certs/ca-bundle.crt)最低 -
CERT_PATH环境变量不被 requests 识别,纯属干扰项
验证是否加载成功:运行 python -c "import requests; print(requests.certs.where())",输出路径应与你期望的一致。若输出的是系统路径而非自定义路径,说明参数或环境变量未生效。
为什么加了证书路径还是报 SSLError: certificate verify failed?
常见原因不是路径错,而是证书内容本身不合法:
- 证书文件是 PEM 格式但混入了空行、Windows 回车(
\r\n)或 BOM 头 —— 用file cacert.pem检查类型,用cat -A cacert.pem查看隐藏字符 - 证书链顺序反了:必须是「服务器证书 → 中间证书 → 根证书」从上到下拼接,倒序会导致验证断裂
- 证书已过期或尚未生效(用
openssl x509 -in cert.pem -text -noout检查Not Before/Not After) - 目标站点用了 SNI(Server Name Indication),而旧版 OpenSSL(
最稳妥的验证方式:用 curl 测试同一证书路径是否有效 —— curl --cacert /path/to/cacert.pem https://your-api.example.com。如果 curl 成功而 requests 失败,基本可定位为 Python 或 urllib3 的 SSL 配置问题。
这类错误往往卡在证书链完整性或时间戳校验上,而不是网络或代码逻辑问题。多花两分钟用 openssl 检查证书,比反复改 Python 参数更高效。


















