nginx双向认证需四要素共存于同一server块:listen 443 ssl、ssl_certificate与key、ssl_client_certificate指定ca证书、ssl_verify_client on;验证深度须匹配证书链,结果需通过proxy_set_header透传至后端。

在 Nginx 中用 ssl_verify_client 实现真正安全的双向认证,关键不是加一行配置就完事,而是整套 TLS 握手链路必须闭环、可信、可控。配置错误会导致 400 错误、静默放行或验证失效,尤其在金融、政务等场景下风险极高。
四要素缺一不可,必须同在 server 块内
仅写 ssl_verify_client on 是无效的。它必须与以下三项共存于同一个 server 块中:
-
启用 HTTPS 监听:
listen 443 ssl;—— 没有这个,TLS 层根本不启动,验证无从谈起 -
服务端证书与私钥:
ssl_certificate和ssl_certificate_key指向 Nginx 自身的有效证书(含 SAN,域名匹配) -
可信 CA 证书文件:
ssl_client_certificate /path/to/ca.crt必须是签发客户端证书的根 CA 或中间 CA 的 PEM 公钥(仅证书,不含私钥;开头为-----BEGIN CERTIFICATE-----) -
强制验证开关:
ssl_verify_client on;—— 生产环境禁用optional,否则等于没验
验证深度要匹配证书链结构
若客户端证书由“根 CA → 中间 CA → 客户端”三级签发,默认 ssl_verify_depth 1 只能验到中间 CA,无法上溯到根,结果就是 $ssl_client_verify 显示 FAILED: unable to get issuer certificate。
正确做法是:
- 用
openssl verify -CAfile ca.crt client.crt在本地验证证书链是否完整 - 根据实际层级设
ssl_verify_depth 2(根 + 中间)或3(多级中间链) - 确保
ca.crt文件里包含所有必要上级证书(PEM 拼接),且顺序无误
把校验结果和身份信息可靠传给后端
Nginx 不会自动转发证书内容。后端要识别用户、做白名单或时间校验,必须手动透传:
-
proxy_set_header X-Client-Verify $ssl_client_verify;—— 值为SUCCESS/FAILED/NONE,用于判断是否通过 TLS 层校验 -
proxy_set_header X-Client-DN $ssl_client_s_dn;—— 提取CN=xxx,OU=yyy等字段,供后端比对权限 -
proxy_set_header X-Client-Cert $ssl_client_cert;—— Base64 编码的完整证书,可用于后端二次验签或存档审计
注意:$ssl_client_cert 在 ssl_verify_client on 下只对验证成功的请求有效;设为 optional 时,未提供证书该变量为空。
避免常见陷阱:路径控制与静态资源
ssl_verify_client 不能写在 location 块里。Nginx 不允许在 location 中启用双向认证,但可通过“全局开启 + 路径拦截”实现精准管控:
- server 块保持
ssl_verify_client on,确保所有 HTTPS 请求都经历证书握手 - 在特定
location /api/或/admin/中透传头信息;非业务路径(如/static/、/css/)不要强求双向认证——浏览器默认不为子资源发送客户端证书,会导致白屏或 400 错误 - 静态资源应走单向 HTTPS + Token 化 URL、Referer 校验或后端代理鉴权,而非依赖 mTLS











