一、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,将兼容性最好的虚拟主机放在首位