Перейти к содержимому
ecdsa.com
PHPPHP

Сообщение об ошибке

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 оказывается частным или сервер не отправляет промежуточный сертификат.

Почему это происходит

Как исправить

  1. 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. 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. 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. 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'

Связанные ошибки

Открыть всю базу ошибок подписей