证书还没过期,HTTPS 为什么提示不安全?从域名不匹配查到证书链
证书没过期,HTTPS 仍可能因为访问域名不匹配、证书链缺失或客户端不信任而报错。排查时先保存浏览器错误码,核对证书日期、覆盖域名和连接入口,再根据错误检查证书链与实际客户端。 只把证书续期一次,未必能解决这些问题。
这次检查的三个公开站点,返回了同一张叶子证书,到期时间都是 2026 年 12 月 29 日 04:02:55,北京时间。一个正常,一个被 Chrome 拦截,还有一个 Chrome 能打开,OpenSSL 却校验失败。
相同的证书日期,出现了三种结果。下面沿着实际错误往下查,看看每一步应该核对什么,以及查到以后怎样处理。
一、先看这次真实结果:日期正常,校验仍然失败
客户端测试于 2026 年 11 月 2 日 15:21,北京时间进行。目标来自 badssl 提供的公开证书演示站:
| 访问目标 | Chrome 实际结果 | OpenSSL 诊断模式结果 |
|---|---|---|
sha256.badssl.com |
HTTP 200,页面打开 | 0:ok |
wrong.host.badssl.com |
ERR_CERT_COMMON_NAME_INVALID |
62:hostname mismatch |
incomplete-chain.badssl.com |
HTTP 200,页面打开 | 21:unable to verify the first certificate |
浏览器为 Chrome 156.0.8078.48,每个目标使用独立新建的浏览器上下文,保留默认证书校验;命令行使用 OpenSSL 4.0.3,明确设置访问域名的 SNI 和主机名校验,采用 IPv4、TLS 1.2。三组 OpenSSL 连接均到达 104.154.89.105:443。
三次读取到的叶子证书,SHA-256 指纹完全相同,覆盖名称均为 *.badssl.com 与 badssl.com。因此,这次能够直接排除"其中一个拿到的叶子证书已经过期"这个解释。
先看浏览器实际拦住的这一项。

图:2026 年 11 月 2 日实际截图。除了警告标题,还应保留域名和 NET::ERR_CERT_COMMON_NAME_INVALID。
二、报告里先核对这四项,不要只看剩余天数
遇到用户反馈"证书有问题",我会先打开在线 SSL 检测(例如宝塔拨测的 SSL 入口),按用户实际访问的域名检查。重点保留四项:检测域名、证书有效期、SAN 覆盖名称、连接 IP,再看整体校验状态。
下面是 2026 年 10 月 28 日对同一公开演示目标留存的报告。当时剩余有效期为 61 天,整体仍提示"证书无效"。这个历史报告与 11 月 2 日的客户端复核,指向同一个需要继续检查的方向。

图:报告采集于 2026 年 10 月 28 日,剩余 61 天为当时数值。日期正常,只完成了有效性检查的一部分。
使用这类报告时,不要停在"还有多少天"。日期正常以后,继续查证书到底覆盖谁、当前连接读到了哪张证书。把这几项放在一页里,后面与浏览器、服务器配置和命令行核对会方便很多。
如果用户实际访问的是多级子域名,就检查完整名称;不要换成主域名测完以后,认为所有子域名都通过了。浏览器跳转到了另一个域名,也应对最终出错的域名单独核对。
三、第一个原因:通配符没有覆盖到实际访问的域名
wrong.host.badssl.com 返回的证书,在 SAN 中列出了:
DNS:*.badssl.com
DNS:badssl.com
这里的 *.badssl.com 可以匹配 sha256.badssl.com 这样的一级子域名,不能跨两层匹配 wrong.host.badssl.com。主域名 badssl.com 则由另一项明确列出的名称覆盖。通配符匹配范围可对照 RFC 9525 中关于名称匹配的规则。
这次 OpenSSL 对两个目标的实际输出如下。日期、SAN 和指纹一致,校验结果随着访问名称发生了变化。

图:真实命令输出的可读摘录。重点看相同的覆盖名称与不同的主机名校验结果。
需要自己复核时,可以执行:
openssl s_client -4 \
-connect wrong.host.badssl.com:443 \
-servername wrong.host.badssl.com \
-verify_hostname wrong.host.badssl.com \
-verify_return_error -tls1_2 </dev/null
这条命令保留了三个相关条件:连接目标、SNI 名称、准备校验的主机名。对这个公开演示目标,本次严格校验输出 hostname mismatch,进程退出码为 1。检查自己的域名时,将命令中的三处目标一起替换。
查到名称不匹配以后,处理方向通常落在两处:证书没有包含需要的域名,或者这个访问入口绑定了不属于该域名的证书。前者需要补齐名称范围;后者需要修正站点、CDN 或负载均衡上的证书绑定。
有个常见想法是"先跳转到另一个正确域名"。但 HTTPS 要先完成 TLS 握手和证书检查,浏览器才会继续处理 HTTP 跳转。如果入口域名本身就无法通过校验,单改跳转规则仍可能把用户留在警告页。
因此,准备保留的每个 HTTPS 入口,都应先具备正确的证书绑定。证书文件名写得像这个网站,或者控制台显示证书签发成功,都不能替代对外访问时的名称检查。
四、第二个原因:证书没过期,服务器却漏发了中间证书
另一个目标 incomplete-chain.badssl.com,这次在 Chrome 中正常打开,取得 HTTP 200。

图:本次浏览器保持校验开启后打开了页面。这个结果还不能替程序客户端完成验收。
OpenSSL 看到的情况不同:服务端只发来了 1 张证书,诊断模式最终显示 21:unable to verify the first certificate。正常对照站 sha256.badssl.com 则发来 3 张证书,校验通过。

图:两边叶子证书的日期与 SAN 相同,服务端发送的证书列表不同。本例中的 1 张与 3 张是实测数量,不是所有网站都要遵守的固定标准。
要看服务器实际发来了哪些证书,可以使用下面的诊断命令:
openssl s_client -4 \
-connect incomplete-chain.badssl.com:443 \
-servername incomplete-chain.badssl.com \
-verify_hostname incomplete-chain.badssl.com \
-showcerts -tls1_2 </dev/null
-showcerts 展示的是服务端发送的证书列表,不等于已经验证成功的完整信任链。普通 s_client 诊断模式还可能在报错后继续握手,所以需要同时阅读验证错误。正式检查希望遇错终止时,再加入 -verify_return_error。具体参数含义以 OpenSSL 官方手册为准。
这次严格模式在缺失签发者证书处先报告错误 20:unable to get local issuer certificate,进程退出码为 1;上表的错误 21 来自继续诊断后的结果。两种模式都指出当前客户端无法建立所需的验证路径,输出阶段不同,错误编号也可能不同。
Chrome 能打开并不矛盾。浏览器与命令行客户端的信任库、补充中间证书的能力及已有状态可能不同。本次没有进一步确定 Chrome 具体依赖哪种补链机制,因此不能把原因简单写成"浏览器一定用了缓存"。
处理自己的网站时,更稳妥的做法是让服务器发送与叶子证书匹配的中间证书,减少对客户端补链的依赖。Nginx 官方文档也说明了浏览器之间可能出现这种差异,并要求组合证书文件时,站点证书放在中间证书之前。
以 Nginx 为例,在现有站点的 HTTPS server 配置块中核对下面两项。路径是示意,需替换成当前站点的实际文件:
ssl_certificate /etc/nginx/ssl/site/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/site/privkey.pem;
这里关注的是 fullchain.pem 的内容:站点证书在前,后接匹配的中间证书。文件名叫 fullchain,不自动说明里面真的完整;也不需要为了增加数量,把无关证书拼进去。
保存配置后,先检查,再按部署方式重载服务:
nginx -t
检查通过只说明配置能被接受,还需要从外部重新读取证书列表并验证。若由面板、容器或其他系统管理 Nginx,按实际环境执行配置检查与重载,避免改了没有被服务使用的文件。
五、配置看起来都对,再查用户到底连到了哪个入口
网站经过 CDN、负载均衡或反向代理时,证书可能配置在不止一个地方。源站已经更新,不代表浏览器连接到的 TLS 入口也完成了更新;不同入口返回不同证书,也可能表现为部分用户正常、部分用户报错。
这时先保留用户访问的域名、时间、网络和实际连接 IP,再对照证书指纹、SAN 与有效期。证书指纹适合确认是不是同一张证书,但不同入口使用不同的有效证书也可以正常工作,不必要求所有入口的指纹绝对相同。
前面的三个演示站共用 104.154.89.105,但访问名称不同、发送链不同,结果就不同。这也提醒我们:查某个入口时,连接 IP、SNI 和校验名称要分别写清楚。
例如,下面这条命令指定本次实际使用的公共 IP,同时保留正常对照域名的 SNI 和名称校验:
openssl s_client \
-connect 104.154.89.105:443 \
-servername sha256.badssl.com \
-verify_hostname sha256.badssl.com \
-verify_return_error -tls1_2 </dev/null
本次相同条件的校验通过。这个 IP 是 11 月 2 日记录到的地址,后续可能变化;检查自己的站点时,替换为准备核对的入口 IP 和实际域名。
如果直接在浏览器里访问 IP 地址形式的 HTTPS 地址,访问名称本身就变成了 IP,不能据此验收原域名的证书。指定连接地址而保留域名条件,才更接近"这个入口能否正确服务这个网站"的检查。
只有某台电脑或某个程序失败时,还要回到那一侧检查系统时间、CA 信任库与代理。比较客户端实际收到的证书,再判断差异来自服务端入口还是客户端环境,不要先让所有用户安装证书或关闭校验。
六、证书已经通过,页面仍有安全提示时看哪里
如果没有证书拦截页,页面也已打开,却仍有资源或安全相关提示,可以继续查看 Chrome 开发者工具的 Security、Console 和 Network。重点找 HTTPS 页面是否请求了 HTTP 脚本、样式、接口等不安全资源。
这属于另一条排查分支。控制台指出哪个资源,就检查该资源的实际地址、跳转和 HTTPS 支持情况;不能只在主站更换证书,就认为第三方资源也会一起恢复。资源是否被升级或阻止,还与浏览器和资源类型有关,按实际日志验证。
本文前面的两个故障已有明确的名称和证书链证据,应先处理对应问题,再进入页面资源检查,避免一开始就把所有安全提示混在一起。
七、修改自己的站点后,按这五步验收
公开演示站用于复现错误,下面的步骤适用于自己站点修改后的检查。验收从原来报错的域名和客户端开始。
- 重访原来的完整地址。 保留证书校验,确认没有拦截页,跳转目的地正确。记录实际响应与关键页面内容,不以跳过警告后的页面作为通过结果。
- 重新核对对外证书。 查看检测域名、有效期、SAN 和连接入口,确认实际访问名称被覆盖,再对照客户端读到的证书。
- 独立校验证书链和名称。 用 OpenSSL 设置正确的 SNI、主机名与
-verify_return_error,同时查看明确的验证错误和进程状态;有异常时继续检查链与信任库。 - 复查原来失败的入口与客户端。 有多个 IP、CDN 或 IPv6 入口时,按实际业务逐个检查;程序报错就在程序的运行环境中验证,不能只由本机浏览器代替。
- 确认关键资源与业务恢复。 检查控制台、资源和接口,保留域名、时间、入口、证书信息及复测结果,方便后续续期与部署核对。
这次样本里,相同的叶子证书日期没有决定最终访问结果。遇到"没过期却不安全",从实际域名和具体错误查起,通常比继续换一张到期更晚的证书更容易找到处理位置。
常见问题
证书还有几十天有效期,为什么仍然会无效?
有效期只是检查的一部分。实际域名是否在 SAN 内、证书链能否建立到可信根、客户端时间是否正确,以及当前入口返回哪张证书,都会影响校验。本文域名不匹配与缺链目标的叶子证书均未过期。
*.badssl.com 为什么不能覆盖 wrong.host.badssl.com?
通配符只匹配最左侧的一层名称,不能跨越多级子域名。该证书还单独包含 badssl.com,但没有能够匹配 wrong.host.badssl.com 的名称。
Chrome 能打开,接口程序仍然报错,先查什么?
先比较服务端发送的中间证书与两个客户端使用的信任环境。本文缺链目标在 Chrome 中返回 HTTP 200,OpenSSL 诊断模式却得到错误 21;一个浏览器的结果不能代替程序环境的验证。
证书链一定要有三张证书吗?
不一定。本文正常对照站发送三张,缺链目标只发送一张,这是本次观察结果。验收应看能否建立有效的信任路径,不能单按张数判断,也不必把根证书和无关证书全部拼进服务端文件。
为什么更新了源站证书,用户仍然看到旧证书?
需要继续检查实际 TLS 入口。若浏览器连接的是 CDN、负载均衡或另一台服务器,更新源站文件不一定改变该入口提供的证书。保存响应 IP 和证书信息后逐项核对。
OpenSSL 最后一行显示正常,就能宣布通过吗?
还要检查前面的验证错误、所用参数与进程状态。本次严格模式遇到名称或签发者问题时退出码为 1,输出末尾仍可能出现 Verify return code: 0。验收不能只截取这一行。
关闭证书校验后可以访问,是否说明修好了?
只能说明改变校验条件后请求继续进行了。处理完成后,应在校验开启的状态下重新检查域名、证书链与实际业务,确认原来的失败客户端也通过。
证书校验通过,页面里的 HTTP 资源也会自动正常吗?
不一定。页面资源还有各自的地址、跳转和服务器配置,需要按浏览器控制台与网络记录逐项检查。主站证书通过不能替代资源与接口验收。

浙公网安备 33010602011771号