- OpenSSL
- Exact text checked with the real tool
verify error:num=21:unable to verify the first certificate
Also seen in: openssl s_client, Node.js (UNABLE_TO_VERIFY_LEAF_SIGNATURE), npm
In one sentence
OpenSSL could not find the issuer of the server's own certificate, which almost always means the server sends its certificate without the intermediate.
Where you see it
In openssl s_client output when you test a server. It almost always appears right after error 20 at depth 0. The code is X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE in the OpenSSL verification options documentation.
$ openssl s_client -connect shop.example.com:443 -servername shop.example.com </dev/null
Connecting to 192.0.2.20
CONNECTED(000001BC)
depth=0 CN=shop.example.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN=shop.example.com
verify error:num=21:unable to verify the first certificate
verify return:1
depth=0 CN=shop.example.com
verify return:1
---
Certificate chain
0 s:CN=shop.example.com
i:C=US, O=Example CA, CN=Example Issuing CA R1
---
...
Verification error: unable to verify the first certificate
...
Verify return code: 21 (unable to verify the first certificate)
Node.js reports the same situation as UNABLE_TO_VERIFY_LEAF_SIGNATURE with the message “unable to verify the first certificate”.
What it means
The “first certificate” is the server’s own certificate, the leaf certificate. To check its signature, OpenSSL needs the certificate of the CA that signed it: normally an intermediate certificate that the server sends along. OpenSSL could not find that issuer, so it could not verify even the first link of the certificate chain. Under “Certificate chain” you typically see only one entry, 0 s:.
Likely causes
The server sends only its own certificate
By far the most common cause. The server was configured with the certificate file the CA issued (cert.pem, server.crt) instead of the full chain.
Confirm: count the certificates sent.
openssl s_client -connect shop.example.com:443 -servername shop.example.com -showcerts </dev/null 2>/dev/null | grep -c 'BEGIN CERTIFICATE'
1 means the intermediate is missing.
Find the intermediate. Your CA’s documentation lists it, and most certificates also say where to download it:
openssl x509 -in server.crt -noout -ext authorityInfoAccess
Authority Information Access:
CA Issuers - URI:http://ca.example.net/issuing-ca-r1.cer
OCSP - URI:http://ocsp.example.net
Download the “CA Issuers” file. It is often in DER (binary) format; convert it to PEM:
curl -sS -o issuing-ca.cer http://ca.example.net/issuing-ca-r1.cer
openssl x509 -inform DER -in issuing-ca.cer -out issuing-ca.pem
Check that it really is the issuer before you use it:
openssl verify -partial_chain -trusted issuing-ca.pem server.crt
server.crt: OK means the intermediate signed your certificate.
Fix: serve the certificate followed by the intermediate, leaf first:
cat server.crt issuing-ca.pem > fullchain.pem
- nginx:
ssl_certificate /etc/ssl/shop.example.com/fullchain.pem;(with certbot, point it atfullchain.pem). - Apache 2.4.8 and later:
SSLCertificateFilewith the full chain file. - IIS: import the intermediate into the computer’s “Intermediate Certification Authorities” store, then rebind the site.
- Load balancers and CDNs: paste the intermediate into the “certificate chain” field when you upload the certificate.
Reload and check again: you should see 1 s: with the intermediate, and Verify return code: 0 (ok) when the root is trusted.
The server sends the wrong intermediate
Sometimes a chain file holds an intermediate from an older certificate or another CA. Compare the issuer of entry 0 with the subject of entry 1 in the -showcerts output: they must be identical. If not, download the correct intermediate as above and rebuild the chain file.
Check yours
Paste the output of openssl s_client -showcerts. The checker shows that the chain stops after the first certificate and which issuer it needs.
Learn why
- Certificate chains and path validation: what “the first certificate” is and how the path is built.
- Related: verify error:num=20 and curl: unable to get local issuer certificate.
Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.