# cURL error 60：PHP 证书校验失败的原因与修法

> PHP 报 cURL error 60 表示 libcurl 无法验证服务器证书，多数是 PHP 没配置 CA 证书包。应设置 curl.cainfo，而不是关闭 SSL 校验。

Source: https://ssl.sh/zh/errors/php-curl-error-60/
Last updated: 2026-11-10

**`cURL error 60: SSL certificate problem` 的意思是：PHP 的 curl 扩展连上了服务器，但无法验证对方的证书链。** 在 Windows 和自己编译的 PHP 上，通常是 PHP 没配置 CA 证书包；在 Linux 服务器上，更多是对方服务器缺中间证书，或者对方用的是 PHP 不认识的私有根证书。

## 快速修复

多数情况给 php.ini 配上 CA 证书包就能解决。先在报错的同一个环境（网页还是命令行）里，查看 PHP 用的是哪个 CA 文件：

```bash
php --ini
php -i | grep -iE 'curl.cainfo|openssl.cafile|openssl.capath'
php -r 'print_r(openssl_get_cert_locations());'
```

如果 `curl.cainfo` 和 `openssl.cafile` 都是空的，默认文件也不存在，就在实际加载的 php.ini 里设置：

```ini
curl.cainfo = "/etc/ssl/certs/ca-certificates.crt"
openssl.cafile = "/etc/ssl/certs/ca-certificates.crt"
```

Windows 上填从 curl.se 下载的 `cacert.pem` 的路径。改完重启 PHP-FPM、Apache 或 IIS。如果证书包本来就没问题，用 [SSL 检测工具](https://ssl.sh/zh/tools/ssl-checker/)检查对方服务器的证书链。

## 原因

Windows 和自编译 PHP 上最常见的是没配置 CA 证书包；Linux 服务器上更多是对方证书链不完整，或者对方用了私有根证书。

### 1. PHP 没有配置 CA 证书包

Windows 版 PHP、XAMPP、WampServer、phpStudy（小皮面板）以及很多自行编译的版本，自带的 libcurl 都没有可用的 CA 路径。

- **确认：** `openssl_get_cert_locations()` 指向的文件不存在，`curl.cainfo` 为空。
- **修复：** 设置 `curl.cainfo`（curl 扩展使用）和 `openssl.cafile`（`file_get_contents('https://...')` 等流函数使用），都指向有效的证书包。

### 2. 对方服务器证书链不完整

如果只有某一个网站或接口报错、其他都正常，多半是对方没装中间证书。对接一些小型第三方接口时并不少见。

- **确认：** `openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts` 只列出一张证书。
- **修复：** 需要对方服务器装上完整证书链，见[缺少中间证书](https://ssl.sh/zh/errors/missing-intermediate-certificate/)和[证书链补全工具](https://ssl.sh/zh/tools/chain-builder/)。可以把检测结果发给对方运维。

### 3. CA 证书包过旧

很久没更新的 `cacert.pem` 或者没打补丁的系统（如 CentOS 6/7）缺少较新的根证书，访问用新根证书的网站就会失败。

- **修复：** 更新系统的 `ca-certificates` 包，或重新下载 `cacert.pem`。

### 4. 公司代理或内网 CA

做 HTTPS 解密的公司代理、内部接口用的根证书都不在 Mozilla 列表里。

- **修复：** 把 PEM 格式的根证书追加到证书包副本里，让 `curl.cainfo` 指向副本，或者按请求单独指定：

```php
$ch = curl_init('https://intranet.example.com/api');
curl_setopt($ch, CURLOPT_CAINFO, '/etc/ssl/certs/company-bundle.pem');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$body = curl_exec($ch);
```

### 5. 改错了 php.ini

CLI 和 FPM 常用不同的文件，例如 Debian、Ubuntu 上分别是 `/etc/php/8.3/cli/php.ini` 和 `/etc/php/8.3/fpm/php.ini`。

- **修复：** 以网页端 `phpinfo()` 里的“Loaded Configuration File”为准修改。

## 各环境修法

每个环境都要先找到实际生效的 php.ini 或客户端选项，再让它指向可信的 CA 证书包。

**宝塔面板：** PHP 的配置文件在 `/www/server/php/<版本号>/etc/php.ini`，例如 PHP 8.1 是 `/www/server/php/81/etc/php.ini`。可以在面板“软件商店 → PHP → 设置 → 配置修改”里搜索 `curl.cainfo`，填入 `/etc/pki/tls/certs/ca-bundle.crt`（CentOS 系）或 `/etc/ssl/certs/ca-certificates.crt`（Debian/Ubuntu 系），保存后重启 PHP。

**Debian / Ubuntu：**

```bash
sudo apt-get install --reinstall ca-certificates
sudo systemctl restart php8.3-fpm   # 换成你的 PHP 版本
```

**CentOS / RHEL / Alibaba Cloud Linux：** 证书包是 `/etc/pki/tls/certs/ca-bundle.crt`，私有根证书放进 `/etc/pki/ca-trust/source/anchors/` 后执行 `update-ca-trust`。

**Windows、XAMPP、WampServer、phpStudy：** 下载 `cacert.pem`，在 Apache 实际使用的 php.ini 里设置两个参数，然后在控制面板里重启 Apache。

**Docker：** 确认镜像里装了 `ca-certificates`，私有根证书复制进镜像后再执行 `update-ca-certificates`。

**框架：** Guzzle、Laravel 用 `'verify' => '/path/to/bundle.pem'`，Symfony HttpClient 用 `cafile` 选项。Composer 有自己的配置，见 [Composer SSL operation failed](https://ssl.sh/zh/errors/composer-ssl-operation-failed/)。

**WordPress：** 核心的 HTTP 请求用的是 `wp-includes/certificates/` 里自带的证书包，保持 WordPress 更新即可保持证书包最新；直接调用 curl 的插件则使用 PHP 的设置。

## 不要这样做

网上流传的几种绕法会关闭证书校验，或者引入来路不明的信任。

- **`curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false)`。** 这是网上转载最多的“解决方法”，它关闭了证书链校验，任何网络上的攻击者都能冒充接口服务器，支付回调、用户数据都可能被篡改。
- **把 `CURLOPT_SSL_VERIFYHOST` 设为 0。** 关闭域名校验后，任意其他域名的有效证书都会被接受。
- **Guzzle 或 Laravel 里写 `'verify' => false`。** 效果相同，而且经常是“临时测一下”后被带进了生产环境。
- **使用来路不明的 `cacert.pem`。** 只用 curl 项目官方发布的文件或系统包管理器提供的证书包。
