nginx 不支持证书钉扎原生实现,因其作为服务端不参与客户端的公钥哈希校验;它仅能通过统一证书管理、禁用动态链调整及配置 expect-ct/hsts 响应头等方式,配合客户端完成钉扎策略。

Nginx 本身不支持证书钉扎(Certificate Pinning)的原生实现。这是关键前提——它无法像客户端(如浏览器、App)那样在服务端主动校验并强制绑定某张证书的公钥哈希值。
证书钉扎是一种客户端安全机制,用于防止中间人攻击(MITM),其逻辑发生在请求发起方:客户端预先存储目标域名对应证书或公钥的哈希(如 SPKI Fingerprint),后续 TLS 握手时比对服务器实际提供的证书是否匹配。Nginx 作为服务端,只负责提供证书,不参与该验证过程。
但你可能真正想解决的是以下两类典型场景,我们分别说明适配思路:
一、让 Nginx 正确响应“已启用钉扎”的客户端(如 HSTS + HPKP 遗留配置或现代 Expect-CT / Expect-Staple)
虽然 HPKP(HTTP Public Key Pinning)已被主流浏览器弃用(Chrome 69+ 移除),但部分内网系统或合规环境仍存在类似需求。Nginx 可通过响应头配合后端逻辑间接支持:
-
Expect-CT 头(推荐替代方案)
告知浏览器检查证书是否出现在公开的 Certificate Transparency(CT)日志中,增强可信度:add_header Expect-CT "enforce, max-age=86400";
-
Strict-Transport-Security(HSTS)
强制浏览器仅用 HTTPS 访问,是钉扎类策略的基础依赖:add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
注意:这些头仅影响客户端行为,不改变 Nginx 的证书提供逻辑,也不做任何服务端校验。
二、为特定域名提供唯一、可预测的证书链(便于客户端做静态钉扎)
若客户端(如内部 App)需对 api.example.com 进行证书钉扎,关键是确保该域名始终返回同一张证书及其完整信任链,避免因证书轮换、CDN 中继或多实例部署导致公钥哈希变化:
-
统一证书管理
所有服务该域名的 Nginx 实例,必须使用同一份私钥 + 同一张签发证书 + 相同的中间证书顺序(.pem文件需包含完整链)。ssl_certificate /etc/ssl/cert/api.example.com_fullchain.pem; # 包含域名证书 + 中间 CA ssl_certificate_key /etc/ssl/cert/api.example.com.key;
禁用动态证书切换
避免使用ssl_trusted_certificate或 OCSP Stapling 动态调整链;关闭ssl_stapling off,确保每次握手返回完全一致的证书字节流。-
验证输出一致性
使用 OpenSSL 检查服务端实际返回的公钥哈希是否稳定:openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null | \ openssl x509 -pubkey -noout | openssl pkey -pubin -outform der 2>/dev/null | openssl dgst -sha256
输出的 SHA256 哈希值应长期不变——这才是客户端可安全钉扎的目标。
三、绕过限制的补充方案(不推荐生产环境)
极少数场景下,若需服务端主动拒绝非预期证书的客户端连接(反向钉扎),只能借助外部工具:
- 利用 Nginx 的
auth_request模块,将 TLS 会话信息(如证书 Subject、Issuer)转发至一个轻量 API 服务; - 该 API 根据域名白名单 + 预存指纹库做实时比对,返回
200或403; - Nginx 根据结果决定是否放行请求。
但这属于高成本定制方案,违背了 TLS 分层设计原则,且无法防御底层 TLS 层的 MITM,实用性低。
不复杂但容易忽略:真正的证书钉扎控制权在客户端。Nginx 要做的,只是稳定、准确、可审计地交付指定证书。











