Nginx 实现真正安全的 mTLS 必须在同一个 server 块中同时配置:listen 443 ssl http2、ssl_certificate 与 ssl_certificate_key、ssl_client_certificate、ssl_verify_client on;还需匹配 ssl_verify_depth、透传 $ssl_client_verify 等变量,并禁用弱协议与加密套件。

要在 Nginx 的 server 块中配置真正安全的 SSL 双向认证(mTLS),必须同时满足四个硬性条件:启用 HTTPS 监听、提供服务端证书、指定可信 CA 用于验证客户端、并强制校验——缺一不可,否则形同虚设。
核心四要素必须同处一个 server 块
这四项配置必须全部写在同一个 server{...} 内,顺序无关,但遗漏任一都会导致双向认证失效或静默降级:
-
监听 HTTPS 端口:用
listen 443 ssl http2;显式启用 TLS 层;不加ssl标志,整个握手流程不会触发 -
服务端身份凭证:通过
ssl_certificate和ssl_certificate_key指向服务器自己的 PEM 格式证书与私钥 -
客户端信任锚点:用
ssl_client_certificate /path/to/ca.crt;指向根 CA 或中间 CA 的纯净 PEM 文件(仅含-----BEGIN CERTIFICATE-----块,不含私钥、无 BOM、无多余空行) -
强制验证开关:必须设为
ssl_verify_client on;;optional在生产环境等于关闭认证,$ssl_client_verify 永远返回 SUCCESS
验证深度与证书链要匹配
若客户端证书由“根 CA → 中间 CA → 客户端”三级签发,默认 ssl_verify_depth 1 只能验证到中间 CA,无法上溯至根,结果是 $ssl_client_verify 显示 FAILED: unable to get issuer certificate。
- 两级链(根 + 中间):设
ssl_verify_depth 2; - 三级链(根 + 中间1 + 中间2):设
ssl_verify_depth 3; - 本地验证链是否完整:运行
openssl verify -CAfile ca.crt client.crt,输出 OK 才代表链可用
把可信身份可靠传给后端服务
Nginx 不会自动转发客户端证书内容。后端如需做用户识别或细粒度权限控制,必须显式透传关键变量:
-
proxy_set_header X-Client-Verify $ssl_client_verify;—— 值为 SUCCESS / FAILED / NONE,供后端快速判断校验结果 -
proxy_set_header X-Client-DN $ssl_client_s_dn;—— 提取 CN、OU、O 等字段,例如CN=app-prod-01,OU=api,O=MyCorp -
proxy_set_header X-Client-Cert $ssl_client_cert;—— Base64 编码的完整客户端证书(仅在验证成功时可用)
增强安全性的必要补充项
仅开启双向认证还不够,还需收紧协议与加密套件:
- 禁用老旧协议:
ssl_protocols TLSv1.2 TLSv1.3; - 禁用弱加密:
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; - 启用吊销检查(推荐金融/政企场景):
ssl_crl /etc/nginx/ssl/crl.pem; - 错误响应更明确(可选):
error_page 495 496 = @deny_access;配合location @deny_access { return 403 "Invalid or missing client certificate"; }


















