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

> What OpenSSL verify error 20 means, how the depth tells a missing intermediate from an untrusted root, and the exact fix for each case.

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

- OpenSSL
- Exact text checked with the real tool

# verify error:num=20:unable to get local issuer certificate

Also seen in: openssl s\_client, openssl verify, Ruby, PHP, anything built on OpenSSL

**In one sentence:**

OpenSSL found a certificate whose issuer is neither in the chain the server sent nor in your trust store; the depth number tells you which link is missing.

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

## Where you see it

In the output of `openssl s_client` while you test a server, and in `openssl verify` while you test certificate files. The number and text are OpenSSL’s `X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY`, listed in the [OpenSSL verification options documentation](https://docs.openssl.org/3.0/man1/openssl-verification-options/).

A server whose chain ends at a root you do not trust:

```text
$ openssl s_client -connect git.example.com:443 -servername git.example.com </dev/null
Connecting to 192.0.2.10
CONNECTED(000001BC)
depth=1 O=Example Corp, CN=Example Corp Issuing CA
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN=git.example.com
verify return:1
---
Certificate chain
 0 s:CN=git.example.com
   i:O=Example Corp, CN=Example Corp Issuing CA
 1 s:O=Example Corp, CN=Example Corp Issuing CA
   i:O=Example Corp, CN=Example Corp Root CA
...
Verify return code: 20 (unable to get local issuer certificate)
```

The same error from `openssl verify`:

```text
$ openssl verify -CAfile root.pem server.pem
CN=git.example.com
error 20 at 0 depth lookup: unable to get local issuer certificate
error server.pem: verification failed
```

`s_client` reports the error and carries on, so you can still read the chain. Programs that use OpenSSL (curl, Python, Ruby, PHP) stop at this point and show their own version of the message.

## What it means

OpenSSL builds a [certificate chain](https://pkizerotohero.com/handbook/glossary/#certificate-chain) from the server’s certificate upwards. For each certificate it needs the issuer’s certificate: from what the server sent, or from the local [trust store](https://pkizerotohero.com/handbook/glossary/#trust-store). At the depth shown, it found a certificate whose issuer it could not find anywhere. The **depth** tells you where: `depth=0` is the server’s own certificate, `depth=1` the first certificate above it.

## Likely causes

### depth=0: the server does not send its intermediate

If the error is at `depth=0` and `s_client` follows it with `verify error:num=21:unable to verify the first certificate`, the server sent only its own certificate. You see a single `0 s:` entry under “Certificate chain”.

**Fix:** configure the server with its certificate followed by the [intermediate certificate](https://pkizerotohero.com/handbook/glossary/#intermediate-certificate):

```bash
cat server.crt issuing-ca.crt > fullchain.pem
```

Use `fullchain.pem` in nginx’s `ssl_certificate` or Apache’s `SSLCertificateFile`; on IIS, install the intermediate in the computer’s “Intermediate Certification Authorities” store. More detail: [verify error:num=21](https://pkizerotohero.com/errors/openssl-verify-error-21/).

When you test certificate files, the same happens if you forget to give `openssl verify` the intermediate:

```bash
openssl verify -CAfile root.pem -untrusted issuing-ca.pem server.pem
```

### depth=1 or higher: the root is not in your trust store

The server sent its intermediate, but the root that signed it is not one you trust. This is typical for company CAs and [TLS-inspection](https://pkizerotohero.com/handbook/glossary/#tls-inspection) proxies. Test with the root you expect:

```bash
openssl s_client -connect git.example.com:443 -servername git.example.com -CAfile corp-root.pem </dev/null 2>/dev/null | grep 'Verify return code'
```

If this prints `Verify return code: 0 (ok)`, the chain is fine and only trust is missing. Add the root to the system store (`/usr/local/share/ca-certificates/` and `update-ca-certificates` on Debian and Ubuntu, `/etc/pki/ca-trust/source/anchors/` and `update-ca-trust extract` on RHEL), and to the stores of the programs that keep their own (Java, Python’s certifi, Node.js).

### OpenSSL has no trust store at all

If every site fails, even public ones, your OpenSSL may not find any trust store. Check where it looks:

```bash
openssl version -d
```

This prints the `OPENSSLDIR`. If that directory has no `cert.pem` or `certs/` with the public roots, install your system’s CA bundle, or pass one explicitly with `-CAfile /etc/ssl/certs/ca-certificates.crt` (the path on Debian and Ubuntu).

Note

Why does my browser work?

Chrome and Edge can download a missing intermediate from the address in the certificate’s [Authority Information Access](https://pkizerotohero.com/handbook/glossary/#authority-information-access) extension. Firefox ships a list of known public intermediates. OpenSSL, curl built with OpenSSL, Java and Python do neither by default, so they show the server’s mistake that the browser hides.

Warning

Do not switch verification off

Settings such as `SSL_VERIFY_NONE` in code, or `-k` in curl, make this error disappear by accepting any certificate from anyone. Fix the chain on the server or trust the correct root.

## Check yours

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

Paste the certificates from `openssl s_client -showcerts`. The checker shows each link and which issuer is missing.

## Learn why

- [Certificate chains and path validation](https://pkizerotohero.com/handbook/certificate-chains/): depth, issuers and how the path is built.
- [Who do we trust? CAs, trust stores and self-signed certificates](https://pkizerotohero.com/handbook/trust-stores/).
- The same problem in other tools: [curl](https://pkizerotohero.com/errors/curl-unable-to-get-local-issuer-certificate/), [Python](https://pkizerotohero.com/errors/python-certificate-verify-failed/), [npm](https://pkizerotohero.com/errors/npm-unable-to-get-issuer-cert-locally/).

**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-20/).

Last reviewed 20 October 2026Edition 0.9

## Learn why it happens

- [Chapter 13: Certificate chains and path validation](https://pkizerotohero.com/handbook/certificate-chains/)
- [Chapter 12: Who do we trust? CAs, trust stores and self-signed certificates](https://pkizerotohero.com/handbook/trust-stores/)

## Fix it step by step

- [How to check the certificate chain a server sends](https://pkizerotohero.com/guides/check-remote-certificate-chain/)
- [How to trust a private root CA on Linux](https://pkizerotohero.com/guides/add-root-ca-linux/)
- [How to build a test CA with OpenSSL (lab only)](https://pkizerotohero.com/guides/openssl-test-ca/)

## Everything on this topic

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