nginx为多个域名配置不同ssl证书并启用mtls,本质是sni多证书(按域名切换服务端证书)与统一客户端证书校验的叠加;不支持per-domain ca列表,需应用层解析host头和客户端证书实现精细化策略。

Linux 上为多个域名配置不同的 SSL 证书并启用双向加密(mTLS),本质上是两件事的叠加:一是基于 SNI 实现多域名多证书的单向 HTTPS,二是为每个域名单独启用客户端证书校验。这不是 Nginx 或 Apache 的默认行为,需明确区分服务端证书(按域名匹配)和客户端证书(统一或分域校验),且二者逻辑层级不同。
明确双向加密的核心逻辑
双向 TLS(mTLS)要求:服务端提供自己的证书(server certificate),同时强制客户端提供并验证其证书(client certificate)。它不等于“给每个域名配一套独立的双向链路”,而是:
✅ 服务端证书可以按域名动态切换(靠 SNI);
❌ 客户端证书验证通常由全局 CA 信任链控制,Nginx/Apache 不支持 per-server-name 的 client CA 列表(即不能让 shop.example.com 只认 A 根,而 api.example.com 只认 B 根);
⚠️ 真正的“每域名独立双向认证”需靠应用层(如后端服务)解析 TLS 握手后的客户端证书 Subject,并结合请求 Host 头做路由级判断。
Nginx 实现多域名 + 各自服务端证书 + 统一客户端校验
这是最常用、最稳妥的落地方式。所有域名共用同一套客户端 CA 信任链,但各自使用专属服务端证书:
- 确保 Nginx ≥ 1.15.9(SNI 多证书支持)
- 每个
server块独立配置ssl_certificate和ssl_certificate_key - 在所有需要双向认证的
server块中,统一添加:ssl_client_certificate /etc/nginx/ssl/ca-bundle.crt;<br>ssl_verify_client on;<br>ssl_verify_depth 2;
- 可选:用
ssl_ciphers强制包含ECDHE-ECDSA或ECDHE-RSA,确保兼容主流客户端证书签名算法
Apache 实现类似效果(需 mod_ssl)
Apache 不支持 SNI 动态证书切换(依赖 OpenSSL 版本与编译选项),但可通过虚拟主机 + 多监听端口或 IP 绑定实现多证书。双向加密配置更直观:
- 每个
<virtualhost></virtualhost>块设置独立SSLCertificateFile和SSLCertificateKeyFile - 统一启用客户端验证:
SSLVerifyClient require<br>SSLVerifyDepth 2<br>SSLCACertificateFile /etc/apache2/ssl/client-ca.crt
- 若需差异化策略(如仅 admin.example.com 要求 mTLS,其他域名只需单向),可在对应 VirtualHost 中开启,其余保持
SSLVerifyClient optional或关闭
真正分域名的客户端证书策略(需应用层介入)
当业务要求严格隔离——例如客户 A 的设备证书只能访问 client1.brandpartner.net,客户 B 的只能访问 client2.brandpartner.net——Nginx/Apache 本身无法按 SNI 匹配不同 CA Bundle。可行方案是:
- 服务端证书仍按域名区分(SNI 正常工作)
- 客户端证书验证设为
optional(Nginx)或SSLVerifyClient optional_no_ca(Apache),把原始证书信息透传给后端 - 通过
ssl_client_cert(Nginx)或%{SSL_CLIENT_CERT}e(Apache)将 PEM 编码的客户端证书作为 HTTP Header 传递 - 后端服务(如 Node.js/Python/Java)收到请求后,解析 Header 中的证书,提取 CN/O/OU 等字段,再根据 Host 头决定是否放行
不复杂但容易忽略:服务端证书必须由可信 CA 签发(或客户端已预置根证书),而客户端证书的验证逻辑,最终要落在业务身份归属上,不是 Web 服务器能全自动完成的。











