“unable to get local issuer certificate”: find and fix the missing intermediate

The page loads fine in the browser, but curl, a script or a Java application refuses the connection. In most cases the server is missing an intermediate certificate.

Typical messages

curl: (60) SSL certificate problem: unable to get local issuer certificate
git:  SSL certificate problem: unable to get local issuer certificate
Python: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
Node:   Error: unable to get local issuer certificate (UNABLE_TO_GET_ISSUER_CERT_LOCALLY)
Java:   PKIX path building failed: unable to find valid certification path to requested target

They all mean the same: starting from the server certificate, the client cannot build a chain up to a root CA it trusts.

Why it still works in the browser

Browsers are forgiving. If an intermediate is missing, they fetch it via the AIA address in the certificate or take it from their cache because they saw it on another site. curl, Python, Java, Git and most apps do not. So a site working in the browser says nothing about whether the chain is complete.

Three possible causes

1. The server does not send the complete chain (most common)

Only the server certificate was configured, without the intermediate. Check with the SSL check or on the command line:

openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts </dev/null

If the output lists only one certificate (0 s:CN=www.example.com) together with verify error:num=20:unable to get local issuer certificate, the intermediate is missing.

2. The client lacks the root CA

Old Docker images without the ca-certificates package, an outdated Java installation or an old certifi package in Python do not know newer root CAs.

3. A firewall intercepts TLS, or it is an internal CA

Many corporate proxies and firewalls replace certificates with their own, issued by an internal CA. Browsers on company machines know that CA through group policy, but Docker containers, Python and Java do not. The same applies to servers with certificates from your own company CA.

Fix on the server: send the complete chain

# nginx: certificate + intermediate in one file
ssl_certificate     /etc/ssl/example/fullchain.pem;
ssl_certificate_key /etc/ssl/example/server.key;

# Apache 2.4.8+: the chain belongs in SSLCertificateFile
SSLCertificateFile    /etc/ssl/example/fullchain.pem
SSLCertificateKeyFile /etc/ssl/example/server.key

# Let's Encrypt: use fullchain.pem, not cert.pem

On Windows/IIS the intermediate must be in the computer’s “Intermediate Certification Authorities” store. The easiest way is to import a PFX that contains the chain. If you do not have the intermediate, the certificate converter completes the chain via AIA, and the SSL check hands it over directly with “Complete chain in the converter”.

Fix on the client

# Debian/Ubuntu: update root CAs or add your own CA
apt install ca-certificates
cp company-root.crt /usr/local/share/ca-certificates/ && update-ca-certificates

# Python (requests)
pip install --upgrade certifi
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt

# Java 9+
keytool -importcert -cacerts -alias company-root -file company-root.crt

Keep away from curl -k, verify=False or NODE_TLS_REJECT_UNAUTHORIZED=0 as a permanent fix. The connection stays encrypted, but is no longer protected against attackers.

FAQ

Do I have to send the root CA?

No. The server sends the server certificate and intermediates. The root CA must already be present on the client; a root sent along is ignored.

How do I tell whether the server or the client is at fault?

Check the server with the SSL check. If it reports a complete chain, the client is the problem (missing root or corporate proxy). If it reports a missing intermediate, fix the server.

Why does the error only appear in Docker?

Slim images such as alpine or debian-slim often contain no root CAs. Install the ca-certificates package in the Dockerfile or copy your company CA in and run update-ca-certificates.