# OpenSSL::SSL::SSLError certificate verify failed（Ruby）

> Ruby 报 OpenSSL::SSL::SSLError certificate verify failed 表示无法验证服务器证书链。应让 Ruby 用对 CA 证书包或修好服务器证书链，而不是 VERIFY_NONE。

Source: https://ssl.sh/zh/errors/ruby-certificate-verify-failed/
Last updated: 2026-11-10

**`OpenSSL::SSL::SSLError ... certificate verify failed` 的意思是：Ruby 的 OpenSSL 连上了服务器，但没能用它加载的 CA 证书验证服务器的证书链。** 通常是 Ruby 去找的 CA 文件不存在或者过旧，或者对方服务器没有发送中间证书。

## 快速修复

多数情况让 Ruby 用上一个有效的 CA 证书包就能修好。先看你的 Ruby 用的是哪个 OpenSSL、去哪里找 CA 证书：

```bash
ruby -ropenssl -e 'puts OpenSSL::OPENSSL_LIBRARY_VERSION, OpenSSL::X509::DEFAULT_CERT_FILE, OpenSSL::X509::DEFAULT_CERT_DIR'
ls -l "$(ruby -ropenssl -e 'print OpenSSL::X509::DEFAULT_CERT_FILE')"
```

文件不存在时，让 Ruby 使用一个有效的证书包：

```bash
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt   # Debian/Ubuntu 路径
```

gem 和 bundle 报错时，运行 `bundle doctor ssl`（较新的 Bundler）和 `gem update --system`。如果只有某一个域名报错，用 [SSL 检测工具](https://ssl.sh/zh/tools/ssl-checker/)检查它的证书链。

## 原因

Ruby 找不到 CA 文件或 CA 过旧最常见，其次是对方服务器没发中间证书，下面共列出 5 种原因。

### 1. Ruby 的 OpenSSL 找不到 CA 文件

用 rbenv/ruby-build、asdf、rvm 编译的 Ruby 会链接某个特定的 OpenSSL（macOS 上通常是 Homebrew 的）。这个 OpenSSL 没有证书文件，或者升级时被删掉了，所有校验都会失败。

- **确认：** `DEFAULT_CERT_FILE` 指向的路径不存在或是空文件。
- **修复：** 安装或更新该 OpenSSL 对应的 CA 证书包，设置 `SSL_CERT_FILE`，或者重新安装这个 Ruby 版本，让它链接到当前的 OpenSSL。

### 2. CA 证书或 RubyGems 版本过旧

老系统和旧版 RubyGems 缺少较新的根证书。

- **确认：** 报错的网站所用根证书比你的系统或 RubyGems 版本更新。
- **修复：** 更新系统 `ca-certificates` 包，执行 `gem update --system` 和 `bundle update --bundler`。CentOS 7 这类已停止维护的系统，证书包和 OpenSSL 都很旧，长期看应升级操作系统。

### 3. 系统时间不对

RubyGems 指南把系统时间错误列为两大原因之一：时间偏到过去或未来，有效证书也会显示为已过期或尚未生效。

- **修复：** 校正时间并开启 NTP 自动同步。国内服务器可以用阿里云、腾讯云提供的 NTP 服务器。

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

- **确认：** `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/)。

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

不在公开证书包里的根证书需要显式添加：

```ruby
require "net/http"
require "openssl"

store = OpenSSL::X509::Store.new
store.set_default_paths
store.add_file("/etc/ssl/certs/company-root.pem")

http = Net::HTTP.new("intranet.example.com", 443)
http.use_ssl = true
http.cert_store = store
puts http.get("/").code
```

如果整台机器都要信任，就把根证书加进系统信任库。报错文字是 `self-signed certificate in certificate chain` 时，见[那一页](https://ssl.sh/zh/errors/self-signed-certificate-in-certificate-chain/)。

## 各环境修法

不管哪个平台，修法都是更新 OpenSSL 和 CA 证书包，让 Ruby 读到它；私有根证书加进系统信任库或用 `SSL_CERT_FILE` 指定。

**macOS + Homebrew + rbenv/asdf：** 执行 `brew upgrade openssl@3 ca-certificates`，再用 ruby-build 重新安装受影响的 Ruby 版本，让它链接当前的 OpenSSL。临时办法是 `export SSL_CERT_FILE="$(brew --prefix)/etc/ca-certificates/cert.pem"`。

**Debian / Ubuntu：**

```bash
sudo apt-get install --reinstall ca-certificates
sudo cp company-root.crt /usr/local/share/ca-certificates/ && sudo update-ca-certificates
```

**CentOS / RHEL / Alibaba Cloud Linux：** 根证书放进 `/etc/pki/ca-trust/source/anchors/`，再执行 `update-ca-trust`。

**Windows（RubyInstaller）：** 用上面的命令看 Ruby 去哪里找证书，再用 `SSL_CERT_FILE` 指向一个包含公司根证书的证书包。

**Docker：** 镜像里安装 `ca-certificates`，并在 `bundle install` 之前把私有根证书复制进去。

**国内镜像源：** 使用 Ruby China 镜像时，地址保持 `https://`：`bundle config mirror.https://rubygems.org https://gems.ruby-china.com`。换源解决的是速度问题，证书错误仍要按上面的方法处理。

**Faraday / HTTParty：** 都可以指定 CA 文件或证书库（Faraday 用 `ssl: { ca_file: ... }`，HTTParty 用 `ssl_ca_file:`）。

## 不要这样做

关闭校验能让报错消失，但任何人伪造的证书都会被接受。

- **`http.verify_mode = OpenSSL::SSL::VERIFY_NONE`。** 接受任何人的任何证书，账号密码和接口数据都可能被截获。
- **重定义 `OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE`。** 这段老代码会让进程里所有库都不再校验证书。
- **在 `.gemrc` 里写 `:ssl_verify_mode: 0`，或把 gem 源改成 `http://`。** gem 是你要执行的代码，不经认证地下载等于给篡改敞开大门。
