错误信息
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 证书包中找不到签发该服务器证书链的 CA。在 Windows 上,通常是根本没有配置证书包;在其他系统上,则是 CA 为私有 CA,或者服务器没有发送中间证书。
原因
Windows 版 PHP 没有 CA 证书包
常见Windows 版的 PHP 构建不附带 CA 证书包;在 curl.cainfo 指向某个证书包(例如 curl 项目提供的 cacert.pem)之前,所有 HTTPS 请求都会报出这个错误。
私有 CA 或执行 TLS 检查的代理
常见内部 API 和经过代理的连接,其证书链通向的根证书不在 libcurl 使用的证书包中。
服务器没有发送中间证书
偶见基于 OpenSSL 的 libcurl 不会获取缺失的中间证书。
容器镜像中没有(最新的)CA 证书
偶见构建时没有安装 ca-certificates 或使用了旧证书包的自定义 PHP 镜像,连访问公共主机也会失败。
修复方法
- 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:为 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)