How Node and the others word it
| Node 26 tls | UNABLE_TO_VERIFY_LEAF_SIGNATURE: unable to verify the first certificate; if the root CA is installed locally, try running Node.js with --use-system-ca |
|---|---|
| Node 26 fetch | TypeError: fetch failed, with cause UNABLE_TO_VERIFY_LEAF_SIGNATURE: unable to verify the first certificate; if the root CA is installed locally, try running Node.js with --use-system-ca |
| Node 26, intermediate sent, root unknown | UNABLE_TO_GET_ISSUER_CERT_LOCALLY: unable to get local issuer certificate |
| OpenSSL 3.6 s_client, same server | verify error:num=20:unable to get local issuer certificate |
| curl 8.23, same server | curl: (60) SSL certificate OpenSSL verify result: unable to get local issuer certificate (20) |
| Python 3.14 ssl, same server | ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1082) |
Common causes
The server sends only its own certificate
Node looks for the issuer of the server's certificate among the certificates the server sent with it. There were none, so it stops at the first certificate and says so. The Certificate Decoder shows the same thing: one certificate, and its issuer is not in the input.
Input
-----BEGIN CERTIFICATE----- MIIB7jCCAZWgAwIBAgIBBDAKBggqhkjOPQQDAjBBMRgwFgYDVQQKDA9mam9yZHRv b2xzIHRlc3QxJTAjBgNVBAMMHGZqb3JkdG9vbHMgdGVzdCBpbnRlcm1lZGlhdGUw HhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAwMRgwFgYDVQQKDA9mam9y ZHRvb2xzIHRlc3QxFDASBgNVBAMMC3NlcnZlci50ZXN0MFkwEwYHKoZIzj0CAQYI KoZIzj0DAQcDQgAE7ee19O78yqmyOB5x/h4K7/QA6YW32XJesJT8lv2Q+Y0GBR4e r2DpKzlXcNQ5G7VN9GkztuzSnXch/wKr38FQdqOBjjCBizAMBgNVHRMBAf8EAjAA MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATAWBgNVHREEDzAN ggtzZXJ2ZXIudGVzdDAdBgNVHQ4EFgQU/HjqRgLgUEQQDnr1y8nhCcb2baowHwYD VR0jBBgwFoAUMt7RDnv6EJHcKso7bd3VWZC9krowCgYIKoZIzj0EAwIDRwAwRAIg VatubrykL+ZDu2wIXIFzNCA4G6SIsUQnf2mw4c30KXMCICf1jBo0UciVihHiS1KE k6cc1SY9pCBuOPjwOoiC3ob3 -----END CERTIFICATE-----
Certificate Decoder says
leaf · issuer not in the input
The intermediate is sent but the root is not trusted
Here the server did its part, the certificate and its intermediate, and Node words the failure differently: unable to get local issuer certificate. The missing piece is then the root, which must come from the client's own trust store. A private CA, or a machine whose operating system trusts a root that Node's own list does not, causes it.
Input
-----BEGIN CERTIFICATE----- MIIB7jCCAZWgAwIBAgIBBDAKBggqhkjOPQQDAjBBMRgwFgYDVQQKDA9mam9yZHRv b2xzIHRlc3QxJTAjBgNVBAMMHGZqb3JkdG9vbHMgdGVzdCBpbnRlcm1lZGlhdGUw HhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAwMRgwFgYDVQQKDA9mam9y ZHRvb2xzIHRlc3QxFDASBgNVBAMMC3NlcnZlci50ZXN0MFkwEwYHKoZIzj0CAQYI KoZIzj0DAQcDQgAE7ee19O78yqmyOB5x/h4K7/QA6YW32XJesJT8lv2Q+Y0GBR4e r2DpKzlXcNQ5G7VN9GkztuzSnXch/wKr38FQdqOBjjCBizAMBgNVHRMBAf8EAjAA MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATAWBgNVHREEDzAN ggtzZXJ2ZXIudGVzdDAdBgNVHQ4EFgQU/HjqRgLgUEQQDnr1y8nhCcb2baowHwYD VR0jBBgwFoAUMt7RDnv6EJHcKso7bd3VWZC9krowCgYIKoZIzj0EAwIDRwAwRAIg VatubrykL+ZDu2wIXIFzNCA4G6SIsUQnf2mw4c30KXMCICf1jBo0UciVihHiS1KE k6cc1SY9pCBuOPjwOoiC3ob3 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIBzzCCAXWgAwIBAgIBAzAKBggqhkjOPQQDAjA5MRgwFgYDVQQKDA9mam9yZHRv b2xzIHRlc3QxHTAbBgNVBAMMFGZqb3JkdG9vbHMgdGVzdCByb290MB4XDTI2MDEw MTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowQTEYMBYGA1UECgwPZmpvcmR0b29scyB0 ZXN0MSUwIwYDVQQDDBxmam9yZHRvb2xzIHRlc3QgaW50ZXJtZWRpYXRlMFkwEwYH KoZIzj0CAQYIKoZIzj0DAQcDQgAE8S+llYHyI61wNUPj0DPhpIyF+hRxM7dJFQdo lkEFplO8LVCXy7RgHxRttTDP+Od2kJV2tmdXrQXeLPMGOrG0C6NmMGQwEgYDVR0T AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDLe0Q57+hCR 3CrKO23d1VmQvZK6MB8GA1UdIwQYMBaAFLcg9Ms28bssX2hEC2iuZ/lj9P4oMAoG CCqGSM49BAMCA0gAMEUCIQDEsN76EmigQQoI21DUJpyRdSt2nQdhomZ1IAkxNuLe 4wIgL4BbGPQ84ODnLT1cTE2A2olMZgsjQDfiaRDDuW41iS8= -----END CERTIFICATE-----
Certificate Decoder says
leaf · issued by 2 intermediate · issuer not in the input
Another client accepts the same server
Whether a client can complete a chain on its own depends on the client. Node, OpenSSL, curl and Python did not download a missing intermediate in our tests, even from a certificate that named the address where its issuer can be downloaded (CA issuers in the Decoder). A server that leaves the intermediate out fails in them, whatever another client does.
How to fix it
- Make the server send the full chain: its certificate first, then each intermediate, with the root left out. Let's Encrypt clients write that as fullchain.pem; nginx takes it in ssl_certificate.
- See what the server sends with openssl s_client -connect example.com:443 -servername example.com -showcerts, and paste it into the Certificate Decoder: the chain table shows each link and where one is missing.
- If the root is missing on the client, give Node the CA certificate with NODE_EXTRA_CA_CERTS=ca.pem. Node also takes --use-system-ca, the option its own message names, which makes it read the operating system's trusted roots; on Linux it follows SSL_CERT_FILE.
- Do not set NODE_TLS_REJECT_UNAUTHORIZED=0 to make the message go away. It turns off certificate checking for the whole process, and Node prints a warning saying so.