1. 现象描述

最近维护的一个网站出现了一个非常诡异的 Bug。部分用户(包括我自己)在访问网站时,会间歇性地遇到浏览器报错:
ERR_SSL_UNRECOGNIZED_NAME_ALERT(部分浏览器可能提示“未识别的 SSL 证书名称”或 SNI 握手失败)。

最让人头疼的是“间歇性”:

  • 刷新几次可能又正常了。
  • 在有的网络环境下(比如手机 5G 移动网络)高频触发,在公司或家里某些 Wi-Fi 下却完全正常。

2. 深入排查与思考

起初,我以为是 CDN 节点同步问题或者是 Nginx 的 server_name 配置有错。但反复检查证书链、Nginx 配置文件,均未发现任何异常。

直到我检查了域名的 DNS 解析记录服务器端口监听状态(netstat/ss),才终于发现了盲点。

发现一:DNS 开启了 IPv4/IPv6 双栈解析

在域名解析后台,我不仅配置了 A 记录(指向服务器的 IPv4 地址),还配置了 AAAA 记录(指向服务器的 IPv6 地址)。

发现二:Web 服务器只听了一半

回到服务器上执行查看端口命令,发现 Nginx 只监听了 IPv4 的 443 端口,完全忽略了 IPv6

为什么会导致间歇性 SSL 错误?

当一个域名同时拥有 AAAAA 记录时,现代浏览器和操作系统会启用 Happy Eyeballs 算法。它会同时尝试建立 IPv4 和 IPv6 连接,或者优先选择 IPv6。

  1. IPv4 连接成功时: 请求正常到达 Nginx 的 IPv4 443 端口,匹配到正确的 server_name 和 SSL 证书,访问成功。
  2. IPv6 连接成功时: 请求通过 IPv6 抵达服务器。由于我的 Nginx 并没有在 IPv6 的 443 端口上配置对应的虚拟主机(Virtual Host),这就导致:

    • 或者是请求被服务器上其他刚好监听了 IPv6 全局端口的默认 Web 服务接收。
    • 或者是触发了 Nginx 的 default_server 逻辑,由于没有匹配到具体的域名证书,Nginx 无法向浏览器提供正确的 SNI(Server Name Indication)响应。

最终,浏览器因为“拿着 A 域名的请求,却在 IPv6 通道上找不到 A 域名的证书”,憋出了 ERR_SSL_UNRECOGNIZED_NAME_ALERT 错误。


3. 解决方案

解决这个问题的核心,就是让 Web 服务器同时具备 IPv4 和 IPv6 的监听与服务能力

方案 A:修复 Web 服务器配置(推荐)

不需要删除 IPv6 解析,在 Web 服务器配置文件中补上 IPv6 的监听即可。

如果你使用的是 Nginx:

打开你网站的 Nginx 配置文件(通常在 /etc/nginx/conf.d/nginx.conf),在 server 块中,加入 listen [::]:443 ssl;

server {
    # 监听 IPv4 的 443 端口
    listen 443 ssl;
    
    # 新增:监听 IPv6 的 443 端口
    listen [::]:443 ssl; 

    server_name yourdomain.com [www.yourdomain.com](https://www.yourdomain.com);

    ssl_certificate /path/to/your/cert.crt;
    ssl_certificate_key /path/to/your/cert.key;

    # 其他配置...