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 错误?
当一个域名同时拥有 A 和 AAAA 记录时,现代浏览器和操作系统会启用 Happy Eyeballs 算法。它会同时尝试建立 IPv4 和 IPv6 连接,或者优先选择 IPv6。
- IPv4 连接成功时: 请求正常到达 Nginx 的 IPv4 443 端口,匹配到正确的
server_name和 SSL 证书,访问成功。 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;
# 其他配置...