配置Public-Key-Pins头后浏览器还是报证书错误怎么办?

书生シ尚萍 阅读 25

我给网站配置了Public-Key-Pins头,参考了文档写死了当前证书和备用密钥的pin值,但访问时Chrome还是提示“证书无效”。明明证书指纹和includeSubDomains参数都对,是不是哪里漏了?

配置的代码是这样的:

Public-Key-Pins: pin-sha256="示例值"; pin-sha256="备用值"; max-age=518400; includeSubDomains

但控制台报错“HPKP pin验证失败”,服务器是Nginx,重启后缓存也清过了…

难道还要设置report-uri?或者因为测试环境用了自签名证书导致pin无效?其他安全头比如HSTS都正常…

我来解答 赞 4 收藏
二维码
手机扫码查看
2 条解答
令狐春依
你这个问题大概率是测试环境用的自签名证书导致的,HPKP对证书链的要求非常严格,自签名证书基本没法通过验证。建议你先在正式环境配个可信CA签发的证书再试。

另外你的配置确实少了report-uri,虽然不是导致问题的主因,但加上会更好。还有几个点需要注意:

第一,确保你pin的是证书链中正确的那个证书,一般是直接给域名签发的那个,而不是中间证书或者根证书。可以通过下面这个命令提取证书的pin值,确认下是不是配错了:
openssl x509 -in 你的证书.crt -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | base64


第二,max-age值别设太大,尤其在调试阶段,建议先改成60秒,不然缓存时间太长改错了都看不到效果。

第三,Nginx配置里记得把Public-Key-Pins这行放到server块里,类似这样:
server {
listen 443 ssl;
server_name 你的域名;

ssl_certificate /path/to/你的证书.crt;
ssl_certificate_key /path/to/私钥.key;

add_header Public-Key-Pins 'pin-sha256="正确的pin值"; pin-sha256="备用pin值"; max-age=60; includeSubDomains; report-uri="https://你的上报地址"';
}


最后提醒一句,HPKP这东西风险挺大,配错了可能导致网站完全不可访问,连回滚的机会都没有。Chrome从2018年起已经不支持HPKP了,现在更推荐用Certificate Transparency机制来增强安全性。实在要配的话,建议先在非核心业务上试试水。
点赞 1
2026-02-17 10:09
皇甫世杰
Public-Key-Pins 头确实容易踩坑,你的配置看着没问题,但浏览器报错“证书无效”或“HPKP pin验证失败”,基本是以下原因导致:

自签名证书的问题:如果你用的是自签名证书,浏览器不会信任它,也就不会认可 pin 的有效性。HPKP 是基于信任链的,根证书必须被浏览器信任才行。用 Let's Encrypt 或其他可信 CA 签发的证书试试。

证书链不完整:Nginx 配置证书时如果没有正确拼接中间证书,会导致证书链不完整,浏览器校验失败。检查你的 crt 文件是否包含完整的证书链。

pin 值计算错误:确保 pin-sha256 的值是当前证书公钥的正确指纹。可以用如下命令生成:
openssl x509 -in your-cert.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64

如果是备用证书,也要对其公钥做同样操作。

是否启用了 HSTS:HPKP 是基于 HSTS 的,必须先设置 HSTS 头,否则浏览器不会执行 pin 校验:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

report-uri 是可选项:不设置不会影响 pin 生效,但加上它可以在出问题时上报,方便调试。

浏览器缓存问题:HPKP 一旦生效,浏览器会缓存很久,即使你改了配置也不会马上清除。建议测试阶段把 max-age 设成小值(如 300)。

如果以上都确认没问题,可以尝试用 curl -I https://yourdomain 或浏览器开发者工具的“Security”标签看证书详情和响应头是否匹配。
点赞 6
2026-02-05 16:02