apache通过sni技术解决单ip多https域名证书冲突,核心是客户端tls握手初期明文发送域名,服务器据此加载对应证书;需满足mod_ssl启用、openssl≥0.9.8f、每个virtualhost独立配置专属证书与私钥、禁用全局证书设置,并通过curl或openssl命令验证sni生效。

Apache 通过 SNI(Server Name Indication)技术解决同一 IP 下多个 HTTPS 域名的证书冲突问题。核心不是“避免冲突”,而是让客户端在 TLS 握手初期就告诉服务器它想访问哪个域名,Apache 再据此加载对应证书——前提是客户端支持、服务端配置正确、证书不混用。
确保 Apache 和 OpenSSL 支持 SNI
这是前提,缺一不可:
- Apache 必须启用
mod_ssl模块(运行a2enmod ssl或确认LoadModule ssl_module modules/mod_ssl.so已加载) - 链接的 OpenSSL 版本需 ≥ 0.9.8f,生产环境推荐 ≥ 1.0.2 或 1.1.1(支持 TLS 1.2/1.3 和稳定 SNI 回调)
- 检查是否满足:执行
httpd -M | grep ssl看到ssl_module (shared);再运行openssl version查版本
为每个域名配置独立的 VirtualHost 块
不能把多个 SSLCertificateFile 写在同一个 <virtualhost></virtualhost> 里——Apache 只认最后一个,必然导致证书错配。必须分开定义:
- 每个域名一个
<virtualhost></virtualhost>,且都明确设置ServerName(如ServerName site-a.com) - 各自指定专属证书和私钥:
SSLCertificateFile /path/to/site-a.crt、SSLCertificateKeyFile /path/to/site-a.key - 若含中间证书,用
SSLCertificateChainFile或合并进.crt文件(推荐后者) - 所有块监听相同地址和端口:
Listen 443+<virtualhost></virtualhost>,依赖 SNI 区分
避免 fallback 行为:禁用未匹配时的默认证书
当客户端不发 SNI(如旧版 IE6/Android 4.0 WebView),Apache 会把第一个加载的 <virtualhost></virtualhost> 当作 fallback,返回它的证书——这会造成其他域名显示“证书不匹配”。应对方法:
- 在所有域名块之前,单独定义一个兜底
<virtualhost></virtualhost>,并加default_server标识 - 该兜底块的
ServerName设为_,证书用仅含服务器 IP 的自签名证书(不含任何业务域名) - 响应设为
return 444(静默关闭连接),或重定向到 HTTP/维护页,避免泄露主站信息
验证 SNI 是否真正生效
光配对不等于跑通,建议实测:
- 用 curl 测试指定 SNI:
curl -v --resolve site-a.com:443:YOUR_IP https://site-a.com,观察返回的证书 CN/SAN 是否匹配 - 用 OpenSSL 手动触发 SNI:
openssl s_client -connect YOUR_IP:443 -servername site-b.com -showcerts - 浏览器访问各域名,点击地址栏锁图标 → 查看证书详情,确认主体名称与当前域名一致
- 检查 Apache 错误日志:
tail -f /var/log/apache2/error.log,留意是否有SSL handshake failed或证书加载失败提示











