Laravel Guzzle报cURL error 77证书验证位置错误求解决方案
环境信息
- 操作系统:Ubuntu 16.04
- PHP版本:7.2
- CURL版本:
curl 7.47.0 (x86_64-pc-linux-gnu) libcurl/7.47.0 GnuTLS/3.4.10 zlib/1.2.8 libidn/1.32 librtmp/2.3 - Guzzle版本:6.3
我的项目依赖AWS、Mailgun等基于Guzzle的包,但频繁抛出以下cURL错误:
error: cURL error 77: error setting certificate verify locations: CAfile: /etc/ssl/certs CApath: /etc/ssl/certs
以下是php.ini中的相关配置片段:
[curl] ; A default value for the CURLOPT_CAINFO option. This is required to be an ; absolute path. curl.cainfo='/etc/ssl/certs/ca-certificates.crt' [openssl] ; The location of a Certificate Authority (CA) file on the local filesystem ; to use when verifying the identity of SSL/TLS peers. Most users should ; not specify a value for this directive as PHP will attempt to use the ; OS-managed cert stores in its absence. If specified, this value may still ; be overridden on a per-stream basis via the "cafile" SSL stream context ; option. openssl.cafile='/etc/ssl/certs/ca-certificates.crt' ; If openssl.cafile is not specified or if the CA file is not found, the ; directory pointed to by openssl.capath is searched for a suitable ; certificate. This value must be a correctly hashed certificate directory. ; Most users should not specify a value for this directive as PHP will ; attempt to use the OS-managed cert stores in its absence. If specified, ; this value may still be overridden on a per-stream basis via the "capath" ; SSL stream context option. openssl.capath='/etc/ssl/certs/'
通过ini_get()确认上述配置已被PHP正确识别,但问题依旧。目前只能通过修改vendor/guzzlehttp/guzzle/src/Client.php,将默认配置改为**'verify' => '/etc/ssl/certs/ca-certificates.crt'**临时解决(显然这不是合理的长期方案)。
ini_get()返回的相关配置信息:
array(8) { ["default_cert_file"]=> string(21) "/usr/lib/ssl/cert.pem" ["default_cert_file_env"]=> string(13) "SSL_CERT_FILE" ["default_cert_dir"]=> string(18) "/usr/lib/ssl/certs" ["default_cert_dir_env"]=> string(12) "SSL_CERT_DIR" ["default_private_dir"]=> string(20) "/usr/lib/ssl/private" ["default_default_cert_area"]=> string(12) "/usr/lib/ssl" ["ini_cafile"]=> string(34) "/etc/ssl/certs/ca-certificates.crt" ["ini_capath"]=> string(15) "/etc/ssl/certs/" } openssl.cafile: /etc/ssl/certs/ca-certificates.crt curl.cainfo: /etc/ssl/certs/ca-certificates.crt
备注:已尝试配置**~/.curlrc及设置环境变量export CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt**,均无效果。
请问有什么有效的解决方案或排查思路?
首先,从错误信息能看出一个关键问题:cURL把/etc/ssl/certs当成了CAfile(证书文件),而不是CApath(证书目录)——这说明要么路径解析出了问题,要么Guzzle/PHP cURL没有正确读取到你配置的证书文件路径。下面是几个实用的排查方向和解决办法:
1. 先确认CA证书文件本身没问题
第一步先排查最基础的:证书文件是否存在、权限是否正确。
运行以下命令检查文件状态:
ls -l /etc/ssl/certs/ca-certificates.crt
如果文件不存在,重新安装系统证书包:
sudo apt-get update && sudo apt-get install --reinstall ca-certificates
还要确保Web服务器的运行用户(比如www-data)能读取这个文件:
sudo chmod 644 /etc/ssl/certs/ca-certificates.crt
2. 检查PHP cURL的编译配置
有时候PHP的cURL扩展是用系统默认证书路径编译的,会忽略php.ini里的配置。你可以通过以下命令查看cURL的编译参数:
php -i | grep -A 10 "cURL Information"
如果输出里的CAINFO或CAPATH不是你在php.ini中设置的值,可能需要重新编译PHP cURL扩展,或者在代码里强制指定证书路径(后面会说)。
3. 全局配置Guzzle的verify选项(别再改vendor文件了!)
修改vendor目录的文件确实是饮鸩止渴,你可以在项目里全局配置Guzzle的默认选项,覆盖它的默认verify设置:
方法1:初始化客户端时统一指定
在所有创建Guzzle客户端的地方,统一加上verify参数:
$client = new \GuzzleHttp\Client([ 'verify' => '/etc/ssl/certs/ca-certificates.crt', // 其他你的全局配置 ]);
方法2:设置Guzzle全局默认选项
如果项目里多处创建客户端,更高效的方式是设置全局默认:
use GuzzleHttp\Client; // 全局设置所有Guzzle客户端的verify参数 Client::setDefaultOption('verify', '/etc/ssl/certs/ca-certificates.crt');
这样不管在哪里创建客户端,都会自动使用这个证书路径,不用每次都写。
4. 确保环境变量被Web服务器进程读取到
你试过设置CURL_CA_BUNDLE,但要注意:终端里export的环境变量,Web服务器进程(Apache/Nginx)是读不到的。你需要在服务的配置文件里设置:
- Apache:编辑
/etc/apache2/envvars,添加一行:
然后重启Apache:export CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crtsudo service apache2 restart - Nginx:在你的fastcgi配置块里添加:
重启Nginx:fastcgi_param CURL_CA_BUNDLE /etc/ssl/certs/ca-certificates.crt;sudo service nginx restart
5. 检查.curlrc的位置和权限
~/.curlrc的问题在于,Web服务器运行脚本时,~对应的是服务器用户的家目录(比如/var/www/.curlrc),而不是你自己用户的家目录。另外,cURL要求这个文件的权限必须是600(只有所有者可读),否则会忽略它:
# 切换到www-data用户(假设服务器用这个用户) sudo su www-data # 创建.curlrc并设置权限 echo "cacert=/etc/ssl/certs/ca-certificates.crt" > ~/.curlrc chmod 600 ~/.curlrc exit
6. 用原生PHP cURL测试,排除Guzzle的问题
先确认是不是Guzzle的问题,写个简单的原生cURL脚本测试:
<?php $ch = curl_init("https://api.mailgun.net/v3/domains"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 手动指定CA文件 curl_setopt($ch, CURLOPT_CAINFO, '/etc/ssl/certs/ca-certificates.crt'); $response = curl_exec($ch); $error = curl_error($ch); curl_close($ch); if ($error) { echo "cURL Error: " . $error; } else { echo "连接成功!"; } ?>
如果这个脚本能成功运行,说明问题出在Guzzle的配置上;如果还是报错,那就是PHP cURL或者系统级别的问题,需要回到前面的步骤排查。
内容的提问来源于stack exchange,提问作者user2663561

