Node.js HTTPS API首证书校验失败错误排查方案咨询
UNABLE_TO_VERIFY_LEAF_SIGNATURE错误的触发原因是HTTPS服务端未返回完整的SSL证书信任链:你当前的Node.js HTTPS配置仅加载了域名对应的叶子证书,没有附带Ionos颁发的中间CA证书。客户端发起请求时,无法构建从站点叶子证书到本地信任库内置根证书的校验路径,即使证书是正规CA签发,也会校验失败。
切HTTP虽然能绕过证书问题,但Adalo等平台强制要求HTTPS接口,这条路走不通。
1. 准备完整的证书材料
从Ionos证书管理后台下载对应域名的证书包,选择适配Nginx/Node.js的压缩包,解压后你会得到两类文件:
- 你当前已在使用的域名叶子证书(即现有
cert.cer) - 1~2个Ionos中间CA证书,文件名通常包含
intermediate、CA chain字样
不需要下载根证书,根证书默认存在所有客户端的信任存储中,服务端无需发送。
2. 修改HTTPS服务配置
Node.js的https.createServer支持直接传入证书数组,不需要手动拼接证书文件,你只需要把证书按「叶子证书在前,中间CA证书按信任层级从低到高排序」的顺序传入cert字段即可,修改后的服务配置代码如下:
https.createServer({ key: fs.readFileSync('./key.key'), cert: [ fs.readFileSync('./cert.cer'), // 域名叶子证书,保持原有路径不变 fs.readFileSync('./ionos-intermediate-ca.cer') // 替换为你下载的Ionos中间证书实际路径,存在多级中间证时按顺序追加到数组中即可 ], passphrase: process.env.DBPASSWORD }, app).listen(PORT, () => { console.log(`HTTPS Server is running on public port ${PORT}.`); })
如果你不想改代码,也可以直接把中间CA证书的文本内容完整追加到现有cert.cer文件末尾,保持原有代码逻辑不变也能生效,注意追加顺序不能乱:先放叶子证书内容,再依次放下级、上级中间CA证书内容。
3. 修复本地定时任务的证书问题
你代码中cron定时任务通过https://localhost:5000调用自身接口,会因为域名不匹配(localhost和证书绑定的公网域名不一致)触发证书错误,这里不需要禁用TLS校验,直接新增一个仅监听本地回环地址的HTTP服务用于内部调用即可,不会暴露到公网,也没有安全风险:
// 原有公网HTTPS服务保持上述修改后的配置不变 const INTERNAL_CALL_PORT = 5001 // 内部服务仅绑定127.0.0.1,公网无法访问 app.listen(INTERNAL_CALL_PORT, '127.0.0.1', () => { console.log(`Internal service running on 127.0.0.1:${INTERNAL_CALL_PORT}`) }) // 修改定时任务请求地址为本地HTTP地址 cron.schedule('0 3 * * *', () => { console.log("Starting schedule ", new Date()) axios.post(`http://127.0.0.1:${INTERNAL_CALL_PORT}/api/v1/freeDates/booking/all`) })
服务重启后,在本地执行以下命令校验证书链是否配置正确:
openssl s_client -connect 你的API公网域名:对应服务端口 -showcerts
如果输出结果末尾显示Verify return code: 0 (ok),说明证书链配置完整,此时Adalo、Integromat等第三方平台调用接口就不会再抛出证书校验错误。
- 不要通过设置
NODE_TLS_REJECT_UNAUTHORIZED=0或者给axios加rejectUnauthorized: false的方式绕过校验,这会完全关闭TLS证书验证,存在中间人攻击风险,而且你也无法修改第三方平台的请求配置,这个方法对解决当前问题无效。 - 证书续期时记得同步更新对应的中间CA证书,不要沿用旧版本的中间证书,否则会再次出现校验失败问题。
- 不要把根证书加入服务端返回的证书列表,只会增加SSL握手的传输体积,没有任何实际作用。
内容的提问来源于stack exchange,提问作者HowWhat

