Apache的mod_ssl不直接实现SNI,而是依赖OpenSSL在ClientHello明文阶段解析SNI域名,再匹配对应VirtualHost并加载证书;需满足OpenSSL≥0.9.8f、客户端支持SNI、每个域名配置独立VirtualHost且明确指定ServerName与证书路径。
apache 的 mod_ssl 本身不实现 sni,而是靠 openssl 在 tls 握手初期读取 clienthello 中的明文域名字段,再由 apache 根据该域名匹配对应的 <virtualhost *:443> 块并加载其证书。配置关键不是“开启 sni 开关”,而是确保整个链路满足 sni 工作前提,并正确组织虚拟主机与证书的绑定关系。
确认基础环境已就绪
缺一不可:
- Apache 必须加载
mod_ssl.so(检查httpd -M | grep ssl或配置中LoadModule ssl_module modules/mod_ssl.so未被注释) - OpenSSL 版本 ≥ 0.9.8f(生产建议 ≥ 1.0.2 或 1.1.1,Windows 下注意 phpEnv 自带的
libcrypto-1_1-x64.dll是否真实存在) - 客户端支持 SNI(IE6/WinXP、Android 4.0 以下 WebView 等旧环境不支持,现代浏览器和 curl 7.18.1+ 均支持)
- Apache 配置中不能有多个
<VirtualHost *:443>绑定同一 IP:Port 却未设ServerName—— 否则会 fallback 到第一个加载的虚拟主机证书
为每个域名写独立的 VirtualHost 块
不要用 ServerAlias 堆多个域名到一个 HTTPS 虚拟主机里。SNI 要求每个域名有明确归属:
- 每个域名(如
site-a.test、site-b.test)必须对应一个单独的<VirtualHost *:443> - 每个块内必须设置
ServerName(推荐也加ServerAlias补全 www/non-www 变体) - 每个块必须指定完整证书路径:
SSLCertificateFile、SSLCertificateKeyFile,以及可选的SSLCertificateChainFile(或合并进证书文件)
示例片段(追加到 httpd-vhosts.conf):
<VirtualHost *:443>
ServerName site-a.test
ServerAlias www.site-a.test
DocumentRoot "/var/www/site-a"
SSLEngine on
SSLCertificateFile "/etc/ssl/certs/site-a.crt"
SSLCertificateKeyFile "/etc/ssl/private/site-a.key"
SSLCertificateChainFile "/etc/ssl/certs/ca-bundle.crt"
</VirtualHost>
<VirtualHost *:443>
ServerName site-b.test
ServerAlias www.site-b.test
DocumentRoot "/var/www/site-b"
SSLEngine on
SSLCertificateFile "/etc/ssl/certs/site-b.crt"
SSLCertificateKeyFile "/etc/ssl/private/site-b.key"
SSLCertificateChainFile "/etc/ssl/certs/ca-bundle.crt"
</VirtualHost>
通配符或 SAN 证书的复用方式
若多个子域共用一张通配符证书(如 *.example.com),仍需为每个子域写独立 <VirtualHost>,但证书路径可复用:
-
shop.example.com和api.example.com可指向同一组SSLCertificateFile和SSLCertificateKeyFile - 前提是证书确实包含这些域名(通配符只覆盖一级子域,
*.example.com不覆盖example.com或a.b.example.com) - 若要同时保护根域,证书需额外加入
example.com到 SAN(Subject Alternative Name)中
验证 SNI 是否真正生效
别只看浏览器——它会自动 fallback 或缓存。用命令行直击握手层:
- 查指定域名是否返回对应证书:
openssl s_client -connect your-server-ip:443 -servername site-a.test -showcerts 2>/dev/null | openssl x509 -noout -text | grep "Subject:" - 换域名再试一次,确认 Subject CN 或 SAN 字段变化
- 去掉
-servername参数重试,应看到 fallback 证书(即第一个定义的虚拟主机证书)


















