一、SNI的工作原理
在传统的SSL/TLS握手过程中,服务器在加密通信建立之前,无法知道客户端要访问哪个域名,因此只能为每个IP地址+端口(通常是443)的配对提供一张默认证书。
SNI(Server Name Indication) 是SSL/TLS协议的扩展,它允许客户端在握手的最初阶段(Client Hello消息中)将目标域名以明文形式发送给服务器。这样一来,服务器就能在返回证书之前,根据这个域名信息,从多个虚拟主机中精准匹配到对应的配置,并返回正确的证书。
一个关键点:SNI在握手阶段(HTTPS加密之前)就完成了域名传递和证书选择,因此与后续的HTTP请求头中的`Host`字段无关。
二、Apache中多证书配置的实现
在Apache中启用SNI并配置多证书,需要满足以下条件并完成相应配置:
1. 服务器端环境要求
组件 最低版本要求
Apache 2.2.12 及以上
OpenSSL 0.9.8f 及以上(实际建议1.0.2+)
mod_ssl 必须安装并启用
主流Linux发行版(RHEL 6+/CentOS 6+/Debian 6+/Ubuntu 10.04+)均原生支持SNI。
2. 虚拟主机配置示例
每个域名需要独立的`<VirtualHost *:443>`块,并各自指定证书和私钥:
```apache
# 第一个站点
<VirtualHost *:443>
ServerName www.site-a.com
SSLEngine on
SSLCertificateFile /path/to/site-a.crt
SSLCertificateKeyFile /path/to/site-a.key
</VirtualHost>
# 第二个站点
<VirtualHost *:443>
ServerName www.site-b.com
SSLEngine on
SSLCertificateFile /path/to/site-b.crt
SSLCertificateKeyFile /path/to/site-b.key
</VirtualHost>
```
注意:Apache 2.4+ 不再需要 `NameVirtualHost *:443` 指令。
三、客户端必须支持SNI
SNI要正常工作,客户端和服务器端都必须支持。
支持SNI的客户端(主流):
现代浏览器:Chrome、Firefox、Safari、Edge等
操作系统:Windows Vista/7+、macOS 10.5.6+、主流Linux、iOS、Android 3.0+
开发库:Java 7u25+、curl 7.18.1+
不支持SNI的典型旧客户端**:
Windows XP上的所有IE版本(IE6/7/8)
Android 2.x及更早版本的系统浏览器
部分嵌入式设备和旧版HTTP客户端库
四、旧设备兼容性问题的应对方案
当客户端不支持SNI时,它不会在握手中发送域名信息。Apache无法知道该返回哪个证书,只能返回配置文件中第一个加载的`:443`虚拟主机的证书——这通常会导致证书域名不匹配的错误。
Apache提供了fallback机制来应对这一问题:
核心配置:在全局SSL配置中设置 `SSLStrictSNIVHostCheck off`
当客户端支持SNI时,Apache正常匹配域名并返回对应证书
当客户端不支持SNI时,Apache使用第一个定义的`:443`虚拟主机的证书响应
最佳实践建议:
1. 将兼容性最强的虚拟主机放在最前面——例如使用通配符证书(`*.example.com`)或多域名SAN证书的站点,这样fallback时返回的证书能覆盖尽可能多的访问域名
2. 为fallback站点准备一张覆盖广泛的证书,而不是自签名或过期证书
3. 如果希望彻底拒绝无SNI的旧客户端,可将 `SSLStrictSNIVHostCheck` 设为 `on`,这样所有不支持SNI的请求都会被拒绝
4. 使用`openssl s_client`命令验证fallback是否生效(不加`-servername`参数模拟无SNI请求)
五、补充:证书链兼容性
除了SNI本身,旧客户端还可能因为根证书不信任而无法建立连接。旧系统(如Windows XP)内置的根证书较旧,新CA签发的证书可能无法被验证。
解决方案是手动构建一条向下兼容的完整证书链——将域名证书与中间证书按顺序合并到一个文件中(`域名证书 → 中间证书 → 更上级中间证书`),让旧客户端能够从证书链回溯到其信任的根证书。
总结
维度 说明
SNI本质 TLS扩展,让客户端在握手阶段明文告知目标域名
服务器要求 Apache 2.2.12+、OpenSSL 0.9.8f+、mod_ssl
客户端要求 现代浏览器/系统均支持;WinXP/IE、Android 2.x不支持
兼容旧设备 设置`SSLStrictSNIVHostCheck off`启用fallback,将兼容性最好的虚拟主机放在首位