エラーメッセージ
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
このエラーの意味
PHP の curl 拡張(Guzzle、Laravel の HTTP クライアント、Symfony HttpClient の curl トランスポートが使用)が、サーバーの証明書を検証できませんでした。libcurl のエラー 60 で、OpenSSL の理由は「unable to get local issuer certificate」です。libcurl のバンドルの中に、サーバーのチェーンを発行した CA が見つかりませんでした。Windows ではバンドルがまったく設定されていないことがよくあります。それ以外の環境では、CA がプライベートなものであるか、サーバーが中間証明書を送信していないかのどちらかです。
発生する理由
CA バンドルのない Windows 版 PHP
よくあるWindows 向けの PHP ビルドには CA バンドルが同梱されていません。curl.cainfo でバンドル(たとえば curl プロジェクトの cacert.pem)を指定するまで、すべての HTTPS リクエストがこのように失敗します。
プライベート CA または TLS を検査するプロキシ
よくある社内の API やプロキシを経由する接続では、チェーンが libcurl の使うバンドルに含まれていないルートにつながっています。
サーバーが中間証明書を送信していない
ときどきOpenSSL を使う libcurl は、不足している中間証明書を取得しません。
(最新の)CA 証明書を含まないコンテナーイメージ
ときどきca-certificates を含めずに、または古いバンドルのままビルドした独自の PHP イメージでは、公開ホストに対しても失敗します。
修正方法
- 1.
curl.cainfo と openssl.cafile を設定する
ext-curl と PHP のストリームが同じバンドルを使うように、php.ini で両方を設定してください。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:verify に CA バンドルを渡す
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 では、PHP の curl と OpenSSL はデフォルトでディストリビューションのバンドルを使います。パスは 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)