<!-- https://pkizerotohero.com/errors/openssl-verify-error-21/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> OpenSSL verify error 21: the server's certificate cannot be checked because its issuer is missing. Find the missing intermediate and serve the full chain.

Source: https://pkizerotohero.com/errors/openssl-verify-error-21/ · Last reviewed: 2026-10-20

- 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.

[Check yours: Chain checker](https://pkizerotohero.com/tools/chain/)

## 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](https://docs.openssl.org/3.0/man1/openssl-verification-options/).

```text
$ 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`](https://pkizerotohero.com/errors/node-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](https://pkizerotohero.com/handbook/glossary/#leaf-certificate). To check its signature, OpenSSL needs the certificate of the CA that signed it: normally an [intermediate certificate](https://pkizerotohero.com/handbook/glossary/#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](https://pkizerotohero.com/handbook/glossary/#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.

```bash
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:

```bash
openssl x509 -in server.crt -noout -ext authorityInfoAccess
```

```text
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:

```bash
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:

```bash
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:

```bash
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.

Note

Why does the site work in my browser?

Chrome and Edge can fetch the missing intermediate from the “CA Issuers” address themselves, and Firefox ships a list of known public intermediates. OpenSSL, curl built with OpenSSL, Java, Python and Node.js do not, by default. So a site can look fine in a browser and still break every script and API client. Always test with `openssl s_client` after a certificate change.

Warning

Do not work around it on the client

Turning verification off, or adding the intermediate to every client’s trust store, hides a server mistake and breaks again at the next renewal. The fix belongs on the server.

## Check yours

*Interactive tool: Chain checker (runs in the browser at [/tools/chain/](https://pkizerotohero.com/tools/chain/))*

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](https://pkizerotohero.com/handbook/certificate-chains/): what “the first certificate” is and how the path is built.
- Related: [verify error:num=20](https://pkizerotohero.com/errors/openssl-verify-error-20/) and [curl: unable to get local issuer certificate](https://pkizerotohero.com/errors/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](https://pkizerotohero.com/contact/?topic=content-error&page=/errors/openssl-verify-error-21/).

Last reviewed 20 October 2026Edition 0.9

## Learn why it happens

- [Chapter 13: Certificate chains and path validation](https://pkizerotohero.com/handbook/certificate-chains/)

## Fix it step by step

- [How to check the certificate chain a server sends](https://pkizerotohero.com/guides/check-remote-certificate-chain/)
- [How to build the full chain file your server needs](https://pkizerotohero.com/guides/build-fullchain/)
- [How to install a certificate in nginx](https://pkizerotohero.com/guides/install-certificate-nginx/)
- [How to install a certificate in Apache httpd](https://pkizerotohero.com/guides/install-certificate-apache/)
- [How to install a certificate in IIS](https://pkizerotohero.com/guides/install-certificate-iis/)
- [How to install a certificate in Tomcat and other Java servers](https://pkizerotohero.com/guides/install-certificate-tomcat/)
- [How to get and renew Let's Encrypt certificates with certbot](https://pkizerotohero.com/guides/certbot-lets-encrypt/)
- [How to automate certificates in Kubernetes with cert-manager](https://pkizerotohero.com/guides/kubernetes-cert-manager/)
- [How to automate certificates on Windows with win-acme](https://pkizerotohero.com/guides/win-acme-windows/)

## Everything on this topic

- [Chains and trust stores](https://pkizerotohero.com/topics/chains-and-trust/)
