PHP cURL报错unable to get issuer certificate 仅生产环境异常本地正常
PHP cURL生产环境报
unable to get issuer certificate错误原因及解决方案 核心错误原因
该错误属于cURL TLS握手阶段的证书链校验失败:cURL无法找到对端返回证书对应的上级签发CA证书,无法构建完整的信任链。由于本地开发环境相同代码运行正常,问题一定出在生产环境和本地环境的配置/网络差异上,常见触发场景如下:
- CA证书路径配置在生产环境失效:代码中配置的
CURLOPT_CAPATH、CURLOPT_CAINFO对应的路径在生产环境不存在、PHP进程无读权限,或指定的CA证书文件内容不完整,缺少中间/根CA证书 - 生产环境系统CA信任库异常:本地开发系统默认带完整的公共根证书库,生产环境如果是精简镜像、长期未更新,可能缺失对应签发机构的根证书,或未安装系统CA证书包
- TLS版本配置不兼容:代码中强制指定
CURL_SSLVERSION_TLSv1(即TLS 1.0版本),目前绝大多数线上服务已经禁用TLS1.0/1.1,低版本TLS握手时的证书链传输逻辑和高版本存在差异,易触发校验失败 - 生产网络存在中间代理:生产环境出口经过WAF、流量网关、负载均衡时,可能对TLS流量做解密重签,本地直连拿到的是原始公开证书,生产环境拿到的是内部网关签发的证书,现有信任库无对应签发者证书
CURLOPT_CAPATH配置未生效:该配置要求指向的目录必须经过opensslc_rehash工具处理生成证书哈希软链接,直接放证书文件不做哈希处理,cURL无法检索到对应CA证书- 代码语法问题:贴出的代码中
CURLOPT_TIMEOUT配置行末尾缺少闭合括号,若生产环境代码存在该语法错误,会导致后续配置不生效,触发异常
排查及解决步骤
按优先级从高到低操作:
- 先获取明确的错误信息
在curl_exec后增加错误打印,确认错误码:
若错误码为60,可确认是证书链校验类问题。if(curl_errno($ch)){ var_dump(curl_errno($ch), curl_error($ch)); } - 校验所有证书文件的可用性
- 生产环境执行
var_dump(is_readable($ssl_cert_path), is_readable($ssl_key_path), is_readable($ca_path.$peer_cert)),确认所有证书、密钥文件对PHP运行进程可读,路径拼接无错误 - 检查
CURLOPT_CAINFO指向的文件内容:必须按证书链顺序,从站点叶子证书开始,依次拼接所有中间CA证书、根CA证书,不能仅存放单张叶子证书 - 未做过
c_rehash处理的证书目录,不要配置CURLOPT_CAPATH,优先单独使用CURLOPT_CAINFO指定完整证书链文件,避免目录检索失败
- 生产环境执行
- 修正不合理的SSL配置
- 删除强制TLS1.0的配置,替换为兼容TLS1.2/1.3的配置:
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_TLSv1_3); - 非双向认证场景,先临时移除
CURLOPT_SSLCERT、CURLOPT_SSLKEY配置,排查客户端证书与CA配置的冲突问题
- 删除强制TLS1.0的配置,替换为兼容TLS1.2/1.3的配置:
- 修复系统CA信任库
- Debian/Ubuntu系统执行以下命令更新根证书库:
apt update && apt install -y ca-certificates - CentOS/RHEL系统执行以下命令更新根证书库:
yum install -y ca-certificates && update-ca-trust - 若生产环境存在内网TLS网关,将网关的根证书追加到系统信任库,或直接合并到
CURLOPT_CAINFO指定的证书文件中
- Debian/Ubuntu系统执行以下命令更新根证书库:
- 对比系统层面的握手差异
在生产服务器命令行直接执行curl测试目标地址:
若命令行请求也报相同的签发者证书错误,说明问题出在系统网络/证书层面,和PHP代码无关;若命令行请求正常,可确定是PHP代码中cURL的证书路径、权限配置错误。curl -v "替换为实际请求的目标URL" - 临时验证手段(禁止生产环境长期使用)
可临时关闭SSL校验确认问题边界:
加上配置后如果请求恢复正常,可100%确认是证书校验环节问题,定位修复后必须删除上述两行配置,关闭校验会存在严重的中间人攻击风险。curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
内容的提问来源于stack exchange,提问作者user7540720
相关产品推荐
相关产品推荐

