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.