对,核心原因就是:客户端访问的域名不在服务器返回的 TLS 证书 SAN 列表中。
但先纠正一点:`SSL_ERROR_BAD_CERT_DOMAIN` 通常是 Firefox 浏览器端的错误码,不是 Apache 自身标准错误日志格式。Apache 的 `error_log` 一般会记录握手失败、SNI/Host 不一致、证书校验失败,例如 `AH02032`、`AH02276` 等。如果你在 Apache 日志里看到这个字符串,可能是应用、代理、WAF、监控脚本写进去的,或者 Apache 作为反向代理校验后端证书时报了类似错误。
常见原因
证书只签了 `example.com`,但用户访问 `https://www.example.com`
证书只签了 `www.example.com`,但用户访问 `https://example.com`
使用通配符 `*.example.com`,但它**不匹配** `example.com`,也**不匹配** `a.b.example.com`
证书没有 SAN 字段,只有旧版 CN;现代浏览器只认 SAN
Apache 443 虚拟主机配置错,SNI 选到了错误证书
客户端 SNI 与 HTTP Host 不一致,例如 SNI 是 `a.com`,Host 是 `b.com`
通过 IP 访问 `https://1.2.3.4`,但证书没有 IP SAN
前面有 CDN、负载均衡、Nginx/HAProxy,用户看到的是边缘证书,边缘证书域名配错
Apache 作为反向代理访问后端 HTTPS,后端证书与 `ProxyPass` 中的主机名不匹配
快速排查
查看服务器实际返回的证书 SAN:
```bash
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -ext subjectAltName
```
重点看输出里是否有:
```text
DNS:example.com
DNS:www.example.com
DNS:*.example.com
```
检查 Apache 虚拟主机和证书配置:
```bash
apachectl -S
apachectl -t -D DUMP_VHOSTS
```
或 RHEL/CentOS:
```bash
httpd -S
```
查看 443 的 `ServerName`、`ServerAlias`、`SSLCertificateFile` 是否对应正确域名。
检查 SNI 和 Host 是否一致:
```bash
curl -vI https://example.com
openssl s_client -connect example.com:443 -servername example.com
```
如果 Apache 错误日志出现类似:
```text
AH02032: Hostname example.com provided via SNI and hostname www.example.com provided via HTTP are different
```
说明 TLS SNI 和 HTTP Host 不一致,需要检查代理、重定向、客户端请求头。
解决办法
1. 重新签发证书,把所有实际访问的域名加入 SAN
例如要同时支持:
```text
example.com
www.example.com
```
用 Let’s Encrypt:
```bash
certbot --apache -d example.com -d www.example.com
```
如果要通配符:
```bash
certbot certonly --manual --preferred-challenges dns \
-d example.com -d '*.example.com'
```
注意:`*.example.com` 不包含裸域 `example.com`,所以通常要同时写。
2. 修正 Apache 443 虚拟主机
```apache
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
</VirtualHost>
```
注意:`ServerName` 和 `ServerAlias` 只是让 Apache 接受这些 Host,**不会让证书自动匹配**。证书 SAN 必须包含这些域名。
3. 统一访问域名
HTTP 可以 301 跳到规范域名,例如:
```apache
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ https://example.com$1 [R=301,L]
```
但 HTTPS 下,TLS 握手发生在 HTTP 之前,所以如果证书本身不匹配,重定向也来不及生效。必须确保证书覆盖所有 HTTPS 入口域名。
4. 多域名多证书依赖 SNI
```apache
<VirtualHost *:443>
ServerName a.example.com
SSLCertificateFile /path/a.crt
SSLCertificateKeyFile /path/a.key
</VirtualHost>
<VirtualHost *:443>
ServerName b.example.com
SSLCertificateFile /path/b.crt
SSLCertificateKeyFile /path/b.key
</VirtualHost>
```
现代浏览器都支持 SNI,但很老的客户端可能只用默认虚拟主机证书。
5. 检查反向代理/CDN
如果 Apache 前面有 CDN、Nginx、负载均衡,要在最外层配置正确证书。
如果 Apache 自己反代后端 HTTPS,检查:
```apache
SSLProxyCheckPeerName on
SSLProxyCheckPeerCN on
```
后端证书必须包含 `ProxyPass` 中使用的主机名。否则要么换成正确域名,要么临时关闭校验,但不推荐。
结论
`SSL_ERROR_BAD_CERT_DOMAIN` 的本质就是:访问域名 ≠ 证书 SAN 中的域名。
解决方向不是改 Apache 日志级别,而是:
让证书 SAN 包含所有实际访问域名;
让 Apache 443 vhost、SNI、HTTP Host 一致;
检查 CDN/反向代理链路中的证书配置;
统一用户访问入口域名。