• 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 at fullchain.pem).
  • Apache 2.4.8 and later: SSLCertificateFile with 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

Try it: Chain checkerOpen the full tool

Runs in your browser. Nothing you paste is sent anywhere.

Every certificate the server sends, leaf first. The output of openssl s_client -showcerts -connect example.com:443 works as it is: we ignore the text around the certificates.

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

In the gameLevel 13: The chainChain Bridge, Keystone. In development

Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.

Last reviewed Edition 0.9