关键是提取已验证证书的元数据并以安全http头透传,前提为启用ssl_verify_client on及ssl_client_certificate,仅在location中用proxy_set_header传递$ssl_client_s_dn、$ssl_client_verify等变量,后端须校验其真实性。

必须先启用并完成客户端证书验证
如果没开启验证,Nginx 根本不会解析客户端证书,所有 $ssl_* 变量都为空,透传也就无从谈起。
-
ssl_verify_client on;— 强制要求客户端提供有效证书(设为optional则允许无证书请求,但此时$ssl_client_verify可能为NONE) -
ssl_client_certificate /path/to/ca-bundle.crt;— 指定用于验证客户端证书签名的 CA(或中间 CA)证书文件 -
ssl_verify_depth 2;— 设置证书链验证深度,一般 1–2 足够,确保能校验到根或可信中间 CA
只在 location 块内用 proxy_set_header 透传关键头字段
这些变量仅在 HTTPS 请求且证书验证通过时才有效,必须写在 location 中(不能放在 http 或 server 全局块),并与 proxy_pass 配合使用。
-
proxy_set_header X-SSL-Client-Cert $ssl_client_cert;— 传递 PEM 格式证书内容(含换行符和空格,注意后端需能正确解析多行 header 或做预处理) -
proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;— 传递证书主题 DN,如/CN=alice/O=Org/C=CN,适合做简单身份匹配 -
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;— 传递验证结果(SUCCESS/FAILED/NONE),后端应首先检查此项是否为SUCCESS -
proxy_set_header X-SSL-Client-Fingerprint $ssl_client_fingerprint;— SHA-256 指纹(Nginx ≥ 1.19.0),比完整证书更轻量,适合白名单校验
后端必须主动校验透传信息的可信性
仅靠 Nginx 传递头字段不等于信任——后端服务必须自行验证这些字段的真实性和一致性,不能直接当作权威身份凭据。
- 检查
X-SSL-Client-Verify: SUCCESS,拒绝FAILED或NONE的请求 - 若使用
X-SSL-Client-Fingerprint,应维护一个合法客户端证书指纹白名单 - 若解析
X-SSL-Client-Cert,需确认其签名可被同一 CA 验证,避免伪造 PEM 内容 - 建议结合
X-SSL-Client-DN中的CN或subjectAltName做权限映射,而非仅依赖 IP 或 Host
特别注意 $ssl_client_cert 的格式问题
该变量值包含原始 PEM 的换行符(\n)和缩进空格,在标准 HTTP header 中非法。多数后端框架会自动忽略或截断,导致证书内容丢失。
- 解决方案一:后端用 base64 编码接收(Nginx 不支持直接编码,需 Lua 模块或改用 OpenResty)
- 解决方案二:只透传指纹和 DN,放弃完整证书,兼顾安全性与兼容性
- 解决方案三:Nginx 用 map 或 Lua 对
$ssl_client_cert做 URL-safe 编码(需额外模块)











