# OpenSSL error 20: unable to get local issuer certificate

> OpenSSL error 20 means the issuer of a certificate in the chain is not in the trust store or supplied intermediates. Read the depth, then add what is missing.

Source: https://ssl.sh/errors/openssl-verify-error-20-unable-to-get-local-issuer/
Last updated: 2026-11-10

**Error 20 means OpenSSL could not find the certificate that issued one of the certificates in the chain.** The depth in the message tells you which link is missing: depth 0 means the leaf's issuer (an intermediate) was not supplied; a higher depth means the chain stops at a CA that is not in your trust store.

## Quick fix

The depth in the error shows which certificate's issuer is missing.

Test the server and look at the depth and the chain it sends:

```bash
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>&1 \
  | grep -E "depth=|verify error|^ *[0-9] s:|Verify return"
```

For local files, give OpenSSL the intermediates and the root explicitly:

```bash
openssl verify -CAfile root.pem -untrusted intermediates.pem leaf.pem
```

- **s_client shows one certificate and error 20 at depth 0:** the server must send its intermediate; see [missing intermediate certificate](https://ssl.sh/errors/missing-intermediate-certificate/).
- **Error 20 at depth 1 or 2:** the top certificate is not in your trust store; add the right root or update the CA bundle.

## Causes

A missing intermediate shows up at depth 0; an untrusted root shows up at a higher depth.

### 1. The intermediate was not supplied (depth 0)

- **Confirm:** only one `s:` line in the s_client output; or with files, `openssl verify leaf.pem` fails but `openssl verify -untrusted intermediates.pem leaf.pem` succeeds.
- **Fix:** on the server, deploy leaf plus intermediates (`fullchain.pem`). With local files, always pass the chain via `-untrusted`. The [chain builder](https://ssl.sh/tools/chain-builder/) finds the correct intermediate for a leaf.

### 2. The root is not trusted (depth ≥ 1)

The chain is complete but ends at a CA your store does not contain: a private CA, a TLS-inspecting proxy, or a new public root on an old system.

- **Confirm:** the last `i:` line in `-showcerts` names a CA that is not in `/etc/ssl/certs/` (or your bundle).
- **Fix:** add the private root to the system store, or pass it with `-CAfile` for the test. Update `ca-certificates` for public roots.

### 3. Wrong trust store in use

A custom `-CAfile` or `SSL_CERT_FILE` replaces the default anchors.

- **Confirm:** the same command works without your `-CAfile` or without the environment variable.
- **Fix:** use a bundle that contains both the public roots you need and any private ones.

### 4. Testing with the wrong file

Verifying an intermediate or a CA bundle file instead of the leaf, or a leaf from a different chain, also produces error 20.

- **Confirm:** `openssl x509 -in file.pem -noout -subject -issuer` shows something other than your server certificate.
- **Fix:** split the bundle and verify the leaf.

## Fixes by environment

At depth 0, install the intermediate on the server; at a higher depth, add the root to the trust store your OpenSSL build reads.

**Debian / Ubuntu:** private roots go in `/usr/local/share/ca-certificates/*.crt`, then `sudo update-ca-certificates`.

**RHEL family:** `/etc/pki/ca-trust/source/anchors/`, then `sudo update-ca-trust`.

**macOS:** the LibreSSL `openssl` shipped with macOS and a Homebrew OpenSSL use different CA files; check `openssl version -d` for the directory each one uses.

**Windows:** OpenSSL for Windows does not read the Windows certificate store; pass `-CAfile` with a PEM bundle exported from the store or downloaded from curl.se.

**Interpreting other tools:** curl error 60, Python `CERTIFICATE_VERIFY_FAILED` and Node `UNABLE_TO_GET_ISSUER_CERT_LOCALLY` are the same OpenSSL error 20 surfaced by those programs; see [curl error 60](https://ssl.sh/errors/curl-60-ssl-certificate-problem/).

## Don't do this

- **Ignore error 20 in s_client because the connection still works.** s_client continues after verification errors by default; real clients do not. Use `-verify_return_error` to make it fail like they do.
- **Add the intermediate as a trusted root** to silence the error. Trust anchors should be roots; trusting an intermediate hides a broken server chain from you but not from your users.
- **Put the leaf certificate into the trust store.** It "works" until the next renewal and then breaks again.
