必须在server块全局启用mTLS(ssl_verify_client on),location内用if($ssl_client_verify != "SUCCESS") { return 403; }二次校验;非敏感路径不加判断;通过日志和响应头记录验证状态。

直接用 if ($ssl_client_verify != "SUCCESS") { return 403; } 拦截非法终端,前提是 Nginx 已在 server 块中启用双向 TLS(mTLS),且 ssl_verify_client on 已设为强制模式。这不是“可选校验”,而是 TLS 层硬性要求客户端提供并验证证书——只有通过验证的请求才允许继续处理。
必须全局启用 mTLS,不能只在 location 中配置
Nginx 不支持在 location 块内设置 ssl_verify_client 或 ssl_client_certificate。这些指令仅在 http 或 server 级生效。若想对 /api/secure/ 等路径强制校验,只能先在 server 块中写:
-
ssl_verify_client on;—— 强制所有 HTTPS 请求携带证书 -
ssl_client_certificate /path/to/ca.crt;—— 指定信任的 CA 根证书 -
ssl_trusted_certificate /path/to/intermediate.crt;(如有中间证书)
这样,任何未带证书、证书无效或被吊销的连接,在 TLS 握手阶段就会失败(返回 HTTP 400 或直接断连),根本不会进入 Nginx 的 HTTP 处理流程。
拦截逻辑要放在目标路径的 location 内
真正执行 return 403 的判断,应放在需保护的路径块中,例如:
location /api/secure/ {
if ($ssl_client_verify != "SUCCESS") {
return 403;
}
proxy_pass http://backend;
}
注意:这里不是“跳过校验”,而是利用已通过 TLS 层验证的上下文,做二次确认。因为 ssl_verify_client on 已确保每个到达该 location 的请求都完成了证书交换,$ssl_client_verify 此时必为 SUCCESS、FAILED:xxx 或 NONE。只要不是 SUCCESS,就说明证书未通过 CA 验签、已过期或被吊销,应立即拒绝。
避免误拦:非敏感路径可跳过验证判断
对于不需要证书认证的路径(如 /public/、/healthz),不要加 if 判断,也不需要修改 $ssl_client_verify 的值。它们会自然绕过校验逻辑,保持兼容性。Nginx 不会对这些路径做额外限制,只要 TLS 握手完成(即使客户端没发证书,ssl_verify_client on 也会让连接失败,所以实际访问这类路径的客户端通常已满足证书要求)。
增强可观测性:记录和暴露校验结果
便于排查和审计,建议在日志和响应头中体现验证状态:
- 在
log_format中加入:$ssl_client_verify $ssl_client_s_dn,方便从 access log 区分合法/非法请求来源 - 测试时临时加响应头:
add_header X-Client-Auth "$ssl_client_verify";,用 curl 查看是否返回SUCCESS - 配合
error_page 495 =403 /403.html;(证书验证失败)和error_page 496 =403 /403.html;(未提供证书),统一错误页面体验


















