nginx双向ssl认证(mtls)需严格匹配三类证书、ca文件格式零容错且默认不校验吊销状态;ssl_verify_client on 返回400/495主因是ca文件格式错误、客户端证书非指定ca直接签发或签名/过期验证失败。

直接说结论:Nginx 双向 SSL 认证(mTLS)不是“开个开关”就能用的,它依赖三类证书严格匹配、CA 文件格式零容错、且默认不校验吊销状态——生产环境若漏掉 ssl_crl 或权限控制,等于裸奔。
ssl_verify_client on 为什么总返回 400 或 495?
这是最常见卡点:Nginx 收到客户端未带证书或证书格式/签名不合法时,直接拒接并返回 400 Bad Request(有时是 495 SSL Certificate Error),而不是透传错误给后端。关键原因有三个:
-
ssl_client_certificate指向的 CA 文件必须是 PEM 格式,且不能含任何空行、BOM、多余空格;OpenSSL 生成的ca.crt若被文本编辑器“美化”过,就会失效 - 客户端证书必须由该 CA **直接签发**(或通过一级子 CA,此时需设
ssl_verify_depth 2),不能是多级中间链但只放根 CA - 证书扩展属性中若含
extendedKeyUsage = clientAuth是推荐项,但 Nginx 不强制检查;真正拦住请求的是签名验证失败或过期
client.crt 怎么生成才不被 Nginx 拒绝?
别用自签 CA 再签客户端证书然后手动拼接——极易出错。正确流程是固定四步:
在无 root/sudo 权限的环境(云容器、VPS、隔离主机)中安装并配置 OpenClaw 浏览器工具的 headless Chrome。适用场景:...
- 用同一份
ca.key+ca.crt签发所有客户端证书,确保ssl_client_certificate /path/to/ca.crt和实际签发者完全一致 - 客户端私钥必须用
openssl genpkey -algorithm RSA -out client.key(非openssl genrsa),避免旧格式兼容问题 - CSR 中
subject最好含唯一标识字段(如CN=app-001),后续可通过$ssl_client_s_dn透传给后端做白名单 - 生成完用
openssl verify -CAfile ca.crt client.crt本地验证,输出OK才算过关
如何让后端服务拿到客户端身份信息?
Nginx 默认不把证书内容转发出去,必须显式透传。常用组合如下:
-
proxy_set_header X-Client-Verify $ssl_client_verify;—— 值为SUCCESS/FAILED/NONE,后端据此判断是否走 mTLS 流程 -
proxy_set_header X-Client-DN $ssl_client_s_dn;—— 客户端证书 Subject 字符串,如CN=mobile-app,OU=API,O=MyBank -
proxy_set_header X-Client-Cert $ssl_client_cert;—— Base64 编码的原始证书(慎用,体积大,仅调试需要) - 注意:
$ssl_client_s_dn中的逗号、等号会被 URL 编码,后端需正确 decode,否则解析 CN 失败
金融级配置里最容易被忽略的两个硬伤
一是 CRL 吊销检查默认关闭:只要客户端证书没过期、签名有效,即使已被 CA 吊销,Nginx 也放行。必须加 ssl_crl /etc/nginx/ssl/ca.crl; 并定期更新 CRL 文件。
二是私钥权限失控:如果 server.key 或 ca.key 被设成 644,任意用户可读,整套双向认证形同虚设。务必执行 chmod 600 /etc/nginx/ssl/*.key 并确认属主为 root:root。










