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

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.

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

$ 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:

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

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.

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

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 proxies. Test with the root you expect:

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:

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

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 certificates from openssl s_client -showcerts. The checker shows each link and which issuer is missing.

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