HTTPS 证书报错怎么排查:过期、域名不符、链不全

浏览器报「你的连接不是私密连接」时,先分清是过期、域名不符还是链不全——三者的排查入口完全不同。给出 openssl s_client 一条命令拿到服务器真实下发的证书、五步排查顺序、链不全的根因与修法,以及指纹比对和换证顺序。

约 8 分钟读完 · 更新于 2026-10-21 · 文中每条都能在下面的配套工具里动手验证

先分诊:三类报错,三条排查路径

浏览器把所有证书问题都显示成同一句「你的连接不是私密连接」,但背后的排查起点完全不同。先看告警文案:ERR_CERT_EXPIRED 与 ERR_CERT_DATE_INVALID 指向有效期,ERR_CERT_COMMON_NAME_INVALID 指向域名匹配,ERR_CERT_UNTRUSTED_AUTHORITY / unable to get local issuer certificate 指向证书链或根信任。分诊错了会白折腾:域名不符时反复看有效期,或者链不全时去改 DNS,都不会有任何进展。

报错文案 → 真正的原因

报错 / 现象真正的原因先查哪一项
ERR_CERT_DATE_INVALID / 已过期notAfter 已过,或本机时钟错误证书有效期与服务器时间
ERR_CERT_COMMON_NAME_INVALIDSAN 里没有你访问的主机名SAN 的 dNSName 列表
unable to get local issuer certificate服务器只发了叶子证书,没发中间证书服务器证书链拼接顺序
ERR_CERT_UNTRUSTED_AUTHORITY(内网)自签名证书,公司根没装到客户端信任库客户端信任链,不是在服务器改
浏览器报错但 curl 正常curl 用的是自带 CA 包,与系统/浏览器信任库不同两边的 CA 来源差异
部分设备正常部分设备报错中间证书缓存、CDN 节点配置不一致各节点实际下发的链
只有某个域名子域名报错泛域名证书不覆盖多级子域SAN 里是否精确列出了该子域
一条命令拿到服务器真实下发的证书
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

# -servername 必须带上:不带就拿不到 SNI 选中的那张证书
# 若想看服务器发出的完整链(含中间证书):
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
从证书文件里读关键字段
openssl x509 -in cert.pem -noout -subject -issuer -dates
openssl x509 -in cert.pem -noout -ext subjectAltName
openssl x509 -in cert.pem -noout -fingerprint -sha256

# 肉眼比对指纹,必须逐字符一致(冒号可有可无,大小写不敏感)
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -fingerprint -sha256
curl 侧对照
curl -v https://example.com 2>&1 | grep -iE "subject|issuer|expire|certificate|SSL"

# -k 跳过校验,只是看内容用;不要用来"修复"问题
curl -k https://example.com

# 带上自己的 CA 包,验证内网证书链是否完整
curl --cacert /etc/ssl/certs/corp-root-ca.crt https://internal.example.com

五步排查顺序

  1. 确认连到的机器是谁:nslookup 看域名当前解析到哪台 IP;如果是 CDN 或负载均衡,先确认流量落到哪个节点。
  2. 拿到证书本身:openssl s_client 看 subject 和 issuer,确认它就是你预期的那张,而不是默认站点或过期旧证书。
  3. 核对有效期:看 notBefore / notAfter,同时对比本机时间——服务器时钟偏差也会让有效期判断全线出错。
  4. 核对 SAN:查 subjectAltName 里的 dNSName 是否包含你访问的主机名。主流浏览器只认 SAN,CN 里的域名早已忽略。
  5. 核对链:确认服务器把中间证书一起发了出来。ssl 客户端握不上手、或报 unable to get local issuer certificate,基本都是这一项。

域名匹配的真相:SAN、通配符和 IP

三条规则足以覆盖绝大多数域名不符问题。一、主流浏览器只检查 subjectAltName 里的 dNSName,证书 CN 里的域名只作兼容考虑,写在 CN 里但 SAN 没写的域名会直接判不匹配。二、通配符只匹配一级:*.example.com 能覆盖 a.example.com 和 b.example.com,覆盖不了 a.b.example.com;三级域名要单独签或再申请 *.b.example.com。三、IP 访问要求 SAN 里是 iPAddress 类型的条目,写成 dNSName 的 IP 字符串无效。签证书时把所有对外域名一次列全,比日后补签便宜得多。

链不全的根因与修法

  • 现象:浏览器提示不安全,但 curl、Java 客户端或某些老旧设备能正常访问。原因是这些客户端缓存过中间证书,或者自带的信任库层级更宽松。
  • 根因一:服务器配置文件里只放了叶子证书。Nginx 的 ssl_certificate 需要「叶子 + 中间」拼成的 fullchain 文件,缺中间是最高频的原因。
  • 根因二:拼接顺序不对。顺序必须是叶子在最前、中间证书依次向后,根证书通常不发送。
  • 根因三:CDN / 多节点配置不一致。灰度或扩容时漏更新某一个节点,会表现为「部分设备正常部分设备报错」。
  • 修法:把完整链部署上去后,用上面的 s_client -showcerts 命令确认中间证书确实出现在响应里,再逐个节点核对。
  • 自签名证书:不要在客户端关校验(curl -k、浏览器点继续)。正确做法是把公司根证书装进客户端信任库,服务器侧只发叶子和中间。

指纹核对与换证顺序

指纹是判断「这张证书和我手上的是不是同一张」的唯一可靠依据,也是核对服务器是否真的换对了的唯一手段。优先用 SHA-256,指纹命令加 -fingerprint -sha256;SHA-1 指纹已无参考价值。换证不要一步到位,正确顺序是:先在 CA 后台签发新证书并本地验证链完整 → 部署到预发或灰度节点 → 用 s_client 确认线上返回的是新指纹 → 全量替换 → 原证书保留到所有客户端缓存过期之后再下线。同时把到期时间纳入监控告警,提前 30 天提醒;短有效期证书(90 天)配自动续期,别靠人记。

常见问题

curl 报 unable to get local issuer certificate,怎么确认是不是链不全?

用 openssl s_client -connect 域名:443 -servername 域名 -showcerts 看响应里有没有中间证书:只有一张叶子证书就是链不全,服务器侧要部署「叶子 + 中间」的完整链。命令末尾的 </dev/null 不能省,否则会挂住等交互输入。

证书显示还没到期,浏览器为什么仍然报错?

两种常见原因:一是本机或服务器时钟偏差,让有效期判断失效;二是报错根本不是有效期问题,而是域名不符或链不全——浏览器把所有证书错误都显示成同一句话。先看具体告警代码,再看 SAN 和链,别直接归因到过期。

通配符证书 *.example.com 能覆盖 a.b.example.com 吗?

不能。通配符只匹配一级标签。要覆盖 a.b.example.com,需要单独签发该域名,或再申请一张 *.b.example.com 证书。IP 访问同理,SAN 里必须是 iPAddress 类型的条目才算数。

SHA-1 和 SHA-256 证书指纹该用哪个核对?

用 SHA-256。SHA-1 已有可构造的碰撞,核对值本身不再可靠,主流浏览器也不再接受 SHA-1 签发的证书。计算方式是 openssl x509 -in cert.pem -noout -fingerprint -sha256,两边的输出逐字符比对即可。

📂

松开以自动格式化

支持 .json / .sql / .csv / .html / .css / .xml / .yaml 文件