Сообщение об ошибке
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
Также выводится как
- ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
Python добавляет место в исходном коде на C, например «(_ssl.c:1082)»; номер меняется от версии к версии Python и для диагностики ничего не значит. urllib оборачивает ошибку в «<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] …>», а requests и urllib3 — в «Max retries exceeded … (Caused by SSLError(…))».
Python ssl module (OpenSSL) — ssl.SSLCertVerificationError, X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (20)
Что это означает
Модуль ssl в Python попросил OpenSSL проверить сертификат сервера, и OpenSSL не нашёл издателя последнего сертификата цепочки среди сертификатов CA, загруженных в этот SSLContext. Сервер не обязательно настроен неправильно: часто этот Python просто использует не тот пакет сертификатов CA, что ваш браузер или операционная система, а то и пустой.
Почему это происходит
Python с python.org в macOS без установленных сертификатов
частоУстановщик Python для macOS с python.org содержит собственный OpenSSL, который не читает связку ключей macOS. Пока для этой версии Python не запущен «Install Certificates.command», у неё вообще нет сертификатов CA, поэтому каждый HTTPS-запрос завершается этой ошибкой.
Частный CA или прокси с инспекцией TLS
частоСервисы компании и корпоративные прокси используют сертификаты, корневой сертификат которых распространяется на управляемые машины через хранилище ОС. OpenSSL в составе Python не обращается к хранилищу Windows или macOS, а в Linux-контейнерах этого корневого сертификата нет, пока его не добавят.
Сервер не отправляет свой промежуточный сертификат
частоOpenSSL не загружает недостающие промежуточные сертификаты. Если сервер отправляет только конечный сертификат, Python выдаёт ошибку, а в браузерах, которые кешируют и скачивают промежуточные сертификаты, всё выглядит нормально.
CA добавлен в системное хранилище, но библиотека использует certifi
иногдаrequests, httpx, aiohttp, botocore и pip (в устаревшем режиме работы с сертификатами) проверяют сертификаты по пакету сертификатов certifi, который распространяется как пакет Python. Корневой сертификат, установленный через update-ca-certificates или в связку ключей ОС, им не виден.
SSL_CERT_FILE или SSL_CERT_DIR указывает не туда
редкоЭти переменные заменяют пути OpenSSL по умолчанию. Путь, оставшийся от другого инструмента, или пакет сертификатов, где есть только частный корневой сертификат, приводит к ошибкам на публичных сайтах (или наоборот).
Как исправить
- 1.
Сборки python.org для macOS: запустите Install Certificates.command
Скрипт устанавливает certifi и создаёт на него ссылку как на пакет сертификатов для OpenSSL этого Python. После установки другой версии Python скрипт обязательно нужно запустить снова.
bash # macOS, python.org installer: run once per installed Python version "/Applications/Python 3.14/Install Certificates.command" - 2.
Используйте хранилище доверенных сертификатов ОС через truststore
truststore заставляет ssl.SSLContext проверять сертификаты средствами ОС (связка ключей в macOS, хранилище сертификатов Windows, системный пакет сертификатов в Linux), поэтому корневые сертификаты, развёрнутые вашим ИТ-отделом, работают и в Python. Это распространяется и на urllib3, requests и pip, причём pip использует truststore по умолчанию начиная с pip 24.2.
python # pip install truststore==0.10.5 — verify with the OS trust store (Keychain, Windows store, Linux bundle) import truststore truststore.inject_into_ssl() # call once at start-up, before creating connections import urllib.request urllib.request.urlopen("https://api.example.com/") - 3.
Явно загрузите CA для одного клиента
В коде, который обращается к частному сервису, передайте cafile (или cadata) в ssl.create_default_context(). Контекст по-прежнему проверяет имена хостов и сроки действия.
python import ssl, urllib.request ctx = ssl.create_default_context(cafile="/etc/ssl/private-ca/corp-root-ca.pem") urllib.request.urlopen("https://api.example.com/", context=ctx) - 4.
Проверьте и задайте путь к пакету сертификатов по умолчанию
ssl.get_default_verify_paths() показывает, какие файл и каталог использует OpenSSL. SSL_CERT_FILE и SSL_CERT_DIR переопределяют их для процесса; укажите в них пакет сертификатов, который содержит и публичные корневые сертификаты, и ваш CA.
bash # Where does this Python look by default? python3 -c "import ssl; print(ssl.get_default_verify_paths())" # Point OpenSSL-based clients at a bundle that includes your CA export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt - 5.
Владельцам серверов: отправляйте полную цепочку
Если в рукопожатии только один сертификат, а его издатель — публичный промежуточный CA, укажите в конфигурации сервера файл с полной цепочкой.
bash # Server side: 1 certificate in the handshake = intermediate missing openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \ | grep -c 'BEGIN CERTIFICATE'
Связанные ошибки
<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unab…Python urllib.requestMax retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1…requests / urllib3 (Python)There was a problem confirming the ssl certificate: [SSL: CERTIFICATE_VERIFY_FA…pip[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certifi…Python ssl module (OpenSSL)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)