做开发和运维这几年,各种 SSL/TLS 证书报错我基本都踩过一遍。浏览器弹出来的那些红色警告页面,刚开始看着挺唬人,但搞明白原理之后其实都有对应的排查路径。
这篇把我遇到过的几个最常见的错误码整理出来,每个都写了出现场景、排查思路和修复方法,当个速查手册用。
目录
- [ERR_CERT_DATE_INVALID --- 证书过期](#ERR_CERT_DATE_INVALID — 证书过期)
-
- [NET::ERR_CERT_AUTHORITY_INVALID --- CA 不受信任](#NET::ERR_CERT_AUTHORITY_INVALID — CA 不受信任)
-
- [ERR_CERT_COMMON_NAME_INVALID --- 域名不匹配](#ERR_CERT_COMMON_NAME_INVALID — 域名不匹配)
-
- [ERR_SSL_VERSION_OR_CIPHER_MISMATCH --- 协议或加密套件不兼容](#ERR_SSL_VERSION_OR_CIPHER_MISMATCH — 协议或加密套件不兼容)
-
- [ERR_CERT_REVOKED --- 证书被吊销](#ERR_CERT_REVOKED — 证书被吊销)
-
- [Mixed Content 警告 --- 混合内容](#Mixed Content 警告 — 混合内容)
1. ERR_CERT_DATE_INVALID --- 证书过期
什么时候会出现
证书有一个固定的有效期(免费证书通常 90 天,付费证书 1 年),过期之后浏览器就会拦截,弹出这个错误。另外一种不太常见的情况是客户端系统时间不对,比如服务器或者本地电脑时钟偏差太大。
怎么排查
用 openssl 看一下证书的有效期:
bash
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
输出里 notAfter 就是过期时间。如果已经过了,那就是证书确实到期了。
如果证书没过期,检查一下本地系统时间:
bash
date
怎么修
- 证书过期:重新申请或续签证书,替换服务器上的证书文件,reload nginx/apache
-
- 系统时间不对:同步 NTP 时间,
ntpdate pool.ntp.org或者配好 chronyd
这个错误是所有证书问题里最高频的,特别是用免费证书的时候,90 天一到就容易忘。建议搞个到期监控,提前几天提醒自己。
- 系统时间不对:同步 NTP 时间,
2. NET::ERR_CERT_AUTHORITY_INVALID --- CA 不受信任
什么时候会出现
- 用了自签名证书
-
- 证书链不完整,缺少中间证书
-
- 证书是由一个不在浏览器信任列表里的 CA 签发的
开发环境用自签名证书很常见,但放到生产环境就会被浏览器拦。
- 证书是由一个不在浏览器信任列表里的 CA 签发的
怎么排查
bash
openssl s_client -connect example.com:443 -servername example.com
看输出里的 verify return code:
verify return code: 18 (self signed certificate)--- 自签名-
verify return code: 21 (unable to verify the first certificate)--- 缺中间证书
也可以用 SSL Labs 在线检测,它会把证书链问题标得很清楚。
怎么修
- 自签名证书:换成正规 CA 签发的证书。免费的 Let's Encrypt 就够用
-
- 缺中间证书:把中间证书和服务器证书合并成一个 fullchain 文件。nginx 配置里
ssl_certificate指向这个合并后的文件
- 缺中间证书:把中间证书和服务器证书合并成一个 fullchain 文件。nginx 配置里
-
- CA 不受信任:确认 CA 在主流浏览器的信任列表里,或者换一个受信任的 CA
nginx
ssl_certificate /etc/ssl/fullchain.pem; # 包含中间证书
ssl_certificate_key /etc/ssl/privkey.pem;
3. ERR_CERT_COMMON_NAME_INVALID --- 域名不匹配
什么时候会出现
证书里写的域名和你实际访问的域名对不上。最典型的场景:
- 证书签给了
example.com,但你访问的是www.example.com -
- 证书签给了
a.example.com,但你访问的是b.example.com
- 证书签给了
-
- 用 IP 访问,但证书签给的是域名
怎么排查
- 用 IP 访问,但证书签给的是域名
bash
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
看输出里的 SAN(Subject Alternative Name)字段,里面列出了证书覆盖的所有域名。
怎么修
- 如果只是少了
www前缀:重新申请时把example.com和www.example.com都加上 -
- 如果有多个子域名:考虑用通配符证书
*.example.com,一张证书覆盖所有子域名
- 如果有多个子域名:考虑用通配符证书
-
- 如果是 CDN/反代场景:确认回源和边缘节点上配的证书都对
通配符证书在多子域名场景下特别好用,不用每加一个子域名就重新申请。
- 如果是 CDN/反代场景:确认回源和边缘节点上配的证书都对
4. ERR_SSL_VERSION_OR_CIPHER_MISMATCH --- 协议或加密套件不兼容
什么时候会出现
客户端和服务端在 TLS 握手阶段谈不拢,找不到双方都支持的协议版本或加密套件。常见于:
- 服务端还在用 TLS 1.0/1.1,但新版浏览器已经不支持了
-
- 服务端配置的加密套件太旧或太新,和客户端没有交集
-
- 某些老旧设备/系统访问只支持新协议的服务器
怎么排查
- 某些老旧设备/系统访问只支持新协议的服务器
bash
# 检查服务端支持的协议版本
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
# 查看支持的加密套件
nmap --script ssl-enum-ciphers -p 443 example.com
怎么修
nginx 推荐配置(兼顾安全和兼容性):
nginx
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;
TLS 1.0 和 1.1 已经被主流浏览器废弃,不建议再开。如果必须兼容老客户端,可以单独开一个端口走老协议,但要评估安全风险。
5. ERR_CERT_REVOKED --- 证书被吊销
什么时候会出现
证书被 CA 主动吊销了。可能的原因:
- 私钥泄露,你主动向 CA 申请吊销
-
- CA 发现签发过程有问题,批量吊销了一批证书
-
- 域名所有权变更
浏览器通过 OCSP 或 CRL 查询到这张证书已经在吊销列表里,就会拦截。
- 域名所有权变更
怎么排查
bash
# 检查 OCSP 状态
openssl s_client -connect example.com:443 -servername example.com -status 2>/dev/null | grep -A3 "OCSP Response Status"
如果显示 revoked,说明确实被吊销了。
怎么修
被吊销的证书没法恢复,只能重新申请一张。流程:
- 生成新的私钥和 CSR
-
- 向 CA 提交签发请求
-
- 替换服务器上的证书和私钥
-
- reload web 服务
另外建议开启 OCSP Stapling,让服务端主动推送 OCSP 响应,减少客户端查询延迟:
- reload web 服务
nginx
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8;
6. Mixed Content 警告 --- 混合内容
什么时候会出现
页面本身是 HTTPS 加载的,但里面引用了 HTTP 的资源(图片、JS、CSS、iframe 等)。浏览器会:
- 对 JS/CSS/iframe 等"主动混合内容"直接拦截,页面功能可能挂掉
-
- 对图片/视频等"被动混合内容"只是在控制台里报个警告,但地址栏的锁会消失
怎么排查
- 对图片/视频等"被动混合内容"只是在控制台里报个警告,但地址栏的锁会消失
打开浏览器开发者工具(F12),切到 Console 标签页,搜 Mixed Content 关键字,能看到具体是哪个资源还在走 HTTP。
怎么修
- 把资源链接从
http://改成https://,或者用协议相对路径// -
- 如果是第三方资源不支持 HTTPS,考虑下载到本地用自己的 HTTPS 域名提供
-
- 在 nginx 里加一个全局重定向,把所有 HTTP 请求 301 到 HTTPS:
nginx
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
```
- 可以用 CSP 头强制浏览器升级所有请求:
Content-Security-Policy: upgrade-insecure-requests;
---
## 小结
这几个错误码覆盖了日常碰到的绝大多数 HTTPS 证书问题。归纳一下核心思路:
| 错误码 | 核心原因 | 快速定位 |
|--------|---------|---------|
| ERR_CERT_DATE_INVALID | 证书过期或系统时间不对 | `openssl x509 -noout -dates` |
| ERR_CERT_AUTHORITY_INVALID | 自签名/缺中间证书 | `openssl s_client` 看 verify code |
| ERR_CERT_COMMON_NAME_INVALID | 域名不匹配 | 查 SAN 字段 |
| ERR_SSL_VERSION_OR_CIPHER_MISMATCH | 协议/套件不兼容 | `nmap ssl-enum-ciphers` |
| ERR_CERT_REVOKED | 证书被吊销 | OCSP 查询 |
| Mixed Content | HTTPS 页面引用 HTTP 资源 | 浏览器控制台 |
证书过期是最常见的,特别是免费证书 90 天一轮。如果子域名多、部署环境多,手动管理证书很容易遗漏。我自己现在用 ssl.spug.cc 来管通配符证书和到期监控,省了不少事。