Сообщение об ошибке
cURL error 60: SSL certificate problem: unable to get local issuer certificate
Также выводится как
- GuzzleHttp\Exception\ConnectException: cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.se/libcurl/c/libcurl-errors.html) for https://api.example.com:8443/
Guzzle выводит её в виде «cURL error 60: SSL certificate problem: <reason> (see https://curl.se/libcurl/c/libcurl-errors.html) for <url>»; старые релизы Guzzle ссылались на https://curl.haxx.se/libcurl/c/libcurl-errors.html. Причина после «problem:» бывает разной («self-signed certificate in certificate chain», «certificate has expired»). Простой вызов curl_error() возвращает текст после «cURL error 60: ».
PHP — Guzzle (and Laravel HTTP client, Symfony HttpClient on curl) over ext-curl; curl_errno() 60
Что это означает
Расширение curl в PHP (его используют Guzzle, HTTP-клиент Laravel и curl-транспорт Symfony HttpClient) не смогло проверить сертификат сервера: ошибка libcurl 60 с причиной от OpenSSL «unable to get local issuer certificate». libcurl не нашёл в своём пакете сертификатов ни одного CA, который выпустил бы цепочку сервера. В Windows пакет сертификатов часто вообще не настроен; в других системах CA оказывается частным или сервер не отправляет промежуточный сертификат.
Почему это происходит
PHP для Windows без пакета сертификатов CA
частоСборки PHP для Windows поставляются без пакета сертификатов CA; пока curl.cainfo не указывает на такой пакет (например, cacert.pem из проекта curl), каждый HTTPS-запрос завершается этой ошибкой.
Частный CA или прокси с инспекцией TLS
частоЦепочки внутренних API и соединений через прокси ведут к корневым сертификатам, которых нет в пакете сертификатов libcurl.
Сервер не отправляет свой промежуточный сертификат
иногдаlibcurl с OpenSSL не загружает недостающие промежуточные сертификаты сам.
Образы контейнеров без (актуальных) сертификатов CA
иногдаСобственные образы PHP, собранные без ca-certificates или со старым пакетом сертификатов, выдают ошибку даже для публичных хостов.
Как исправить
- 1.
Настройте curl.cainfo и openssl.cafile
Задайте оба параметра в php.ini, чтобы ext-curl и потоки PHP использовали один и тот же пакет сертификатов; помните, что CLI и PHP-FPM часто загружают разные файлы php.ini.
ini ; php.ini — CA bundle for ext-curl (Guzzle, Laravel Http, Symfony HttpClient) and for streams curl.cainfo = "/etc/ssl/certs/ca-certificates.crt" openssl.cafile = "/etc/ssl/certs/ca-certificates.crt" ; Windows: download cacert.pem from https://curl.se/docs/caextract.html and use its full path - 2.
Guzzle: передайте пакет сертификатов CA в verify
Параметр verify принимает путь к файлу CA; Guzzle проверяет, что файл существует, а если его нет, выбрасывает исключение «SSL CA bundle not found».
php // Trust a private CA for one client: verify takes a CA bundle path $client = new GuzzleHttp\Client(['verify' => '/etc/ssl/private-ca/corp-root-ca.pem']); $response = $client->get('https://api.example.com/'); - 3.
Установите CA в системное хранилище
В Linux расширения curl и OpenSSL в PHP по умолчанию используют пакет сертификатов дистрибутива; openssl_get_cert_locations() показывает пути.
bash # Debian/Ubuntu-based PHP images: install the root, then check what PHP uses cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt && update-ca-certificates php -r 'var_dump(openssl_get_cert_locations()); echo ini_get("curl.cainfo"), PHP_EOL;' - 4.
Проверьте, какой php.ini действует
php --ini выводит список загруженных файлов; параметр, добавленный не в тот файл (CLI, FPM или Apache), ни на что не влияет.
bash php --ini # which php.ini files are loaded (CLI and FPM differ!) php -i | grep -E 'curl.cainfo|openssl.cafile'
Связанные ошибки
cURL error 77: error setting certificate file: /etc/ssl/missing-ca.pemPHP ext-curl (Guzzle, Laravel HTTP client)SSL operation failed with code 1. OpenSSL Error messages:PHP streams (file_get_contents, fopen, stream_socket_client, SoapClient, mail libraries)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)