关键是要构建协同生效的验证闭环:ssl_verify_client仅控制校验开关与响应,必须与ssl_certificate、ssl_certificate_key、ssl_client_certificate三者同时正确配置才生效;应按location路径下沉启用,透传X-Client-Verify供后端最终鉴权,避免全站开启导致静态资源访问失败。

要利用 ssl_verify_client 开启双向 TLS(mTLS)身份认证,关键不是单独加这一行指令,而是构建一套协同生效的验证闭环:它只负责“是否校验”和“如何响应”,不提供证书信任链,也不参与解析。真正起作用的是它与服务端证书、CA 证书、作用域配置的配合。
必须配齐的三项基础配置
ssl_verify_client 只有在以下三者同时存在且路径正确时才真正生效:
-
服务端身份凭证:通过
ssl_certificate和ssl_certificate_key指定,让客户端能验证 Nginx 身份; -
可信 CA 列表:用
ssl_client_certificate指向 PEM 格式的根或中间 CA 证书文件(如/etc/nginx/ssl/ca.crt),仅含公钥,不含私钥或注释; -
校验开关指令:设为
on(生产强制)、optional(调试用,需后端二次判断)或off(禁用);optional_no_ca不适用 Nginx,仅 Apache 支持。
按业务路径精准启用,避免误伤静态资源
全站开启 ssl_verify_client on 会导致 CSS、JS、图片等请求也卡在证书选择环节,浏览器通常不自动发送证书,结果大量 403。推荐下沉到敏感接口路径:
- 在
location /api/v1/admin { ... }或location /private/ { ... }块内配置ssl_client_certificate和ssl_verify_client optional; - 用
proxy_set_header X-Client-Verify $ssl_client_verify将验证结果透传给后端; - 后端检查该 header 是否为
SUCCESS再放行,普通接口 location 保持默认单向 HTTPS。
验证状态必须由后端最终确认
Nginx 的 $ssl_client_verify 是可信字段,但仅表示 TLS 层校验通过。不能仅靠它做权限决策:
- 务必在后端代码中严格判断
X-Client-Verify: SUCCESS,拒绝FAILED或空值; - 如需识别具体客户端,可透传
X-Client-DN $ssl_client_s_dn,再提取其中CN、OU等字段做白名单匹配; - 避免在 PHP/Java 中重复解析 PEM 内容,优先使用 Nginx 已导出的结构化变量(如
$ssl_client_fingerprint,Nginx 1.19.6+ 支持)。
常见失效原因与自查要点
若配置后仍提示 “peer did not return a certificate” 或返回 500,大概率是以下问题:
- CA 证书文件路径错误、权限非 600、内容混入空行或注释;
-
ssl_client_certificate指向了私钥或终端用户证书; - 未设置
ssl_verify_depth,而客户端证书由中间 CA 签发(建议设为2并拼全 CA 链); - 客户端未使用由该 CA 签发的有效证书,或证书已过期、被吊销。


















