apache在单ip上为多个域名提供https服务依赖sni技术:客户端tls握手初期明文发送域名,服务器据此匹配virtualhost并加载对应证书;需满足apache≥2.2.12且编译支持openssl、mod_ssl启用、openssl≥0.9.8f、客户端支持sni;每个virtualhost必须独立配置sslcertificatefile和sslcertificatekeyfile,禁用全局证书设置,servername须精确匹配,sslstrictsnivhostcheck off置于全局段以兼容旧客户端。

Apache 在单 IP 上为多个域名提供各自 HTTPS 服务,靠的是 SNI(Server Name Indication)技术。它让客户端在 TLS 握手初期就明文发送请求的域名,Apache 据此匹配对应虚拟主机并加载专属证书。只要环境满足、配置隔离、路径明确,就能稳定运行。
确认底层支持是前提
缺一不可:
- Apache ≥ 2.2.12(推荐 2.4.8+),且编译时启用了 OpenSSL 支持——运行 httpd -V | grep -i ssl 应能输出 OpenSSL 版本信息
- mod_ssl 模块已启用:Debian/Ubuntu 执行 a2enmod ssl;RHEL/CentOS 检查 LoadModule ssl_module modules/mod_ssl.so 是否在配置中启用
- OpenSSL 库版本 ≥ 0.9.8f(实际建议 ≥ 1.0.2 或 1.1.1,以兼容 TLS 1.2/1.3 和稳定回调)
- 客户端需支持 SNI:IE6/WinXP、Android 2.x 等旧环境不支持;现代浏览器、curl 7.18.1+、Java 7u25+ 均支持
每个域名必须独占一套证书文件
SNI 生效的关键是“按域名精确加载”,不是共用,更不能混写:
- 禁止在全局 ssl.conf 或主配置中设置 SSLCertificateFile 或 SSLCertificateKeyFile
- 每个
块内必须显式声明自己的证书与私钥,例如:
SSLCertificateFile /etc/letsencrypt/live/site-a.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/site-a.com/privkey.pem - 若使用中间证书(如 Let’s Encrypt 的 chain),可加 SSLCertificateChainFile(Apache 2.4.8+ 可省略,因 fullchain.pem 已含)
- site-a.com 和 site-b.com 的证书绝不能指向同一个 fullchain.pem 文件,否则所有请求都会返回第一个站点的证书
虚拟主机结构要规范
Apache 2.4 不再需要 NameVirtualHost *:443,但结构必须清晰:
- 所有 HTTPS 虚拟主机统一绑定到 *:443,不可混用 IP 或端口
- 每个块必须有明确的 ServerName,且拼写与客户端 SNI 请求完全一致(大小写不敏感,但域名字符必须精准)
- 建议为每个站点单独建配置文件(如 sites-available/site-a.com.conf),启用时用 a2ensite(Debian/Ubuntu)或手动 Include
- SSLStrictSNIVHostCheck off 应放在全局段(如 ssl.conf 末尾),而非某个 VirtualHost 内部——这是为了兼容无 SNI 的旧客户端,让它 fallback 到首个定义的站点(非错误中断)
验证是否真正生效
别只看浏览器,缓存和 HSTS 会干扰判断:
- 用 OpenSSL 直连测试:
openssl s_client -connect example.com:443 -servername example.com -showcerts
观察返回的证书 Subject 是否匹配该域名 - 对不同域名分别执行上述命令,确认各自返回的证书路径、CN 和有效期均正确
- 检查 Apache 错误日志(ErrorLog)是否有 TLS handshake failure 或 SSL certificate not found 类报错











