How the tools word it
| OpenSSL 3.6 s_client | verify error:num=19:self-signed certificate in certificate chain |
|---|---|
| OpenSSL 3.6 verify | error 19 at 2 depth lookup: self-signed certificate in certificate chain |
| curl 8.23 | curl: (60) SSL certificate OpenSSL verify result: self-signed certificate in certificate chain (19) |
| Node 26 tls | SELF_SIGNED_CERT_IN_CHAIN: self-signed certificate in certificate chain |
| Python 3.14 ssl | ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1082) |
Common causes
The root is a private or test CA
The chain below is complete and in order, and it ends in a root that is not in a public trust store. The error is raised for the third certificate, depth 2 in OpenSSL. A client trusts it only after you add that root.
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----- -----BEGIN CERTIFICATE----- MIIBwzCCAWqgAwIBAgIBATAKBggqhkjOPQQDAjA5MRgwFgYDVQQKDA9mam9yZHRv b2xzIHRlc3QxHTAbBgNVBAMMFGZqb3JkdG9vbHMgdGVzdCByb290MB4XDTI2MDEw MTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowOTEYMBYGA1UECgwPZmpvcmR0b29scyB0 ZXN0MR0wGwYDVQQDDBRmam9yZHRvb2xzIHRlc3Qgcm9vdDBZMBMGByqGSM49AgEG CCqGSM49AwEHA0IABH0DdyM4j2Qm5yUYrP9opJIyAW5oKmYs1KlgD5QaEspetzDt LmYCaEdSk051mmCgtov0ROqWiElrHplTLeJ0vQWjYzBhMB8GA1UdIwQYMBaAFLcg 9Ms28bssX2hEC2iuZ/lj9P4oMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD AgEGMB0GA1UdDgQWBBS3IPTLNvG7LF9oRAtormf5Y/T+KDAKBggqhkjOPQQDAgNH ADBEAiBJvWgbvsYiq9JtmZfKyepnmBnyzFQ93G+Tu9MHiZSt9wIgLz6fgDWJkUSD djhBBe3H4vsZwwTf+tNiFatZWY+Bkno= -----END CERTIFICATE-----
Certificate Decoder says
leaf · issued by 2 intermediate · issued by 3 root · self-signed
The server's own certificate is self-signed
When the leaf itself signs itself, the message loses the words in certificate chain: self-signed certificate, error 18 at depth 0 in OpenSSL, DEPTH_ZERO_SELF_SIGNED_CERT in Node. The cause is the same, a certificate no trusted CA vouches for.
Input
-----BEGIN CERTIFICATE----- MIIBhDCCASmgAwIBAgIBBjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDDAtzZXJ2ZXIu dGVzdDAeFw0yNjAxMDEwMDAwMDBaFw0zNjAxMDEwMDAwMDBaMBYxFDASBgNVBAMM C3NlcnZlci50ZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5AQZINGTVGz2 E4ip0sTj/Q9uZjvCjo8aBDmw2fAZGPEnFKRN8E1iv4qf5LuNdsavJmMZ62Ky4XY9 xCKZleexTaNoMGYwHQYDVR0OBBYEFAcRP2dk59U5vcQBSxUqur3W1NImMB8GA1Ud IwQYMBaAFAcRP2dk59U5vcQBSxUqur3W1NImMAwGA1UdEwEB/wQCMAAwFgYDVR0R BA8wDYILc2VydmVyLnRlc3QwCgYIKoZIzj0EAwIDSQAwRgIhAKsK71tbzp8UA4fs VaX3xgi2yYNRRo211NJ6TpSuV2qMAiEA16zHBGn60FM11g6YiRwqXdsVL5oW6Tf+ 3Z5agmDeGNM= -----END CERTIFICATE-----
Certificate Decoder says
Issuer · Same as the subject · Self-signed
A proxy re-signs the traffic
A company proxy or antivirus that opens TLS traffic ends every chain in its own root. If a public website gives this error only on one network, suspect that, and compare with another network.
How to fix it
- Give the client the CA's root certificate: curl --cacert ca.pem, NODE_EXTRA_CA_CERTS=ca.pem for Node, SSL_CERT_FILE=ca.pem for Python and OpenSSL, or add it to the system's trust store.
- Paste the chain into the Certificate Decoder to see which certificate is the self-signed one and who issued the rest.
- For development, make one private CA and issue every test certificate from it, so you add one root to your machines. mkcert does this.
- If a public site gives the error only on your network, ask the network's owner for the proxy's CA certificate instead of working around it.
- Do not turn verification off (curl -k, NODE_TLS_REJECT_UNAUTHORIZED=0, verify=False). That removes the protection for every server, not just this one.