错误信息
Hostname/IP does not match certificate's altnames: Host: api.example.com. is not in the cert's altnames: DNS:www.example.com
也可能输出为
- Hostname/IP does not match certificate's altnames: IP: 127.0.0.1 is not in the cert's list:
- code: 'ERR_TLS_CERT_ALTNAME_INVALID'
- ERR_TLS_CERT_ALTNAME_INVALID
其中的主机和 altnames 列表按每次失败的实际情况填写。对于 IP 地址,消息文本为“Hostname/IP does not match certificate's altnames: IP: <address> is not in the cert's list: <entries>”。npm 将其显示为“npm error code ERR_TLS_CERT_ALTNAME_INVALID”。
Node.js tls.checkServerIdentity() — code ERR_TLS_CERT_ALTNAME_INVALID
含义
证书链是受信任的,但 tls.checkServerIdentity() 没有找到与 Node.js 所连接主机相匹配的主题备用名称(SAN)。消息列出了双方:你请求的主机,以及证书实际包含的名称,因此一眼就能看出哪里不匹配。如果证书带有 SAN 扩展,通用名称会被忽略。
原因
证书中没有列出这个名称
常见为 www.example.com 签发的证书不覆盖 api.example.com,也不覆盖裸域名;通配符只覆盖一个标签。
通过 IP、localhost 或容器别名连接
常见https://127.0.0.1:8443 或 docker-compose 中的 https://backend:443 这类 URL,请求的是证书中不包含的名称;IP 地址需要 IP 类型的 SAN 条目。
只改了 Host 请求头,没有改 TLS 名称
偶见通过 IP 连接时,把 headers.host 设为正确的名称,并不会改变 Node.js 验证的内容;检查哪个名称、以 SNI 发送哪个名称,由 servername 选项(或 URL 中的主机)决定。
服务器返回了默认证书
偶见如果反向代理没有针对这个名称的配置,就会用另一个网站的证书响应。
修复方法
- 1.
列出证书中的名称
把 subjectAltName 与你所连接的主机对比。
bash echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null \ | openssl x509 -noout -ext subjectAltName - 2.
重新签发证书,包含所有正在使用的名称
加入所有 DNS 名称;如果客户端通过地址连接,还要加入 IP 条目。
bash openssl req -new -key server.key -out server.csr -subj "/CN=api.example.com" \ -addext "subjectAltName=DNS:api.example.com,DNS:www.example.com,IP:10.0.0.5" - 3.
通过地址连接时设置 servername
在 servername(以及 Host 请求头)中保留证书上的名称,然后连接到该地址;这样验证使用的是正确的名称,而不是被关闭。
js // Connect to an address but verify the certificate's real name (also sent as SNI) import https from "node:https"; https.get({ host: "10.0.0.5", port: 443, path: "/", servername: "api.example.com", headers: { host: "api.example.com" } }, (res) => res.resume()); - 4.
用 checkServerIdentity 设置严格限定的例外
对于证书暂时无法重新签发的旧主机,把一个已知别名映射到证书上的名称,再交给 tls.checkServerIdentity() 处理。切勿无条件返回 undefined:那样会彻底关闭名称检查。
js // Narrow exception for a host you cannot reissue: accept one extra name, keep all other checks import https from "node:https"; import tls from "node:tls"; const agent = new https.Agent({ checkServerIdentity: (host, cert) => tls.checkServerIdentity(host === "legacy-alias.internal" ? "api.example.com" : host, cert), });
相关错误
npm error code ERR_TLS_CERT_ALTNAME_INVALIDnpm (Node.js)x509: certificate is valid for example.com, not api.example.comGo crypto/x509curl: (60) SSL: no alternative certificate subject name matches target hostname…curl (OpenSSL backend)[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch,…Python ssl module (OpenSSL host check)