- curl
- Exact text checked with the real tool
SSL certificate problem: unable to get local issuer certificate
Also seen in: git, PHP, other programs built on libcurl
In one sentence
curl could not link the server's certificate to a trusted root: usually the server omits its intermediate, or the root is a private or proxy CA missing from curl's bundle.
Where you see it
curl prints this with exit code 60 (CURLE_PEER_FAILED_VERIFICATION in the libcurl error list). git shows the same text, because git uses libcurl for HTTPS.
$ curl https://api.example.com/
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
$ git clone https://git.example.com/team/app.git
Cloning into 'app'...
fatal: unable to access 'https://git.example.com/team/app.git/': SSL certificate problem: unable to get local issuer certificate
On Windows, the curl.exe that ships with Windows uses Schannel instead of OpenSSL, and the same problem reads differently:
curl: (60) schannel: SEC_E_UNTRUSTED_ROOT (0x80090325) - The certificate chain was issued by an authority that is not trusted.
What it means
A server proves who it is with a certificate chain: its own certificate, signed by an intermediate certificate, signed by a root certificate. curl must find each issuer, either among the certificates the server sent or in its local trust store (a CA bundle file). Here it reached a certificate whose issuer it could not find anywhere, so it could not reach a root it trusts and stopped. A browser may open the same site without complaint: Chrome and Edge can fetch a missing intermediate themselves, Firefox may already have it in its preloaded list, and your company’s private root may be in the browser’s store but not in curl’s bundle.
Likely causes
The server does not send its intermediate certificate
This is the most common cause for public websites and APIs. Count the certificates the server sends:
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null | grep -E '^ [0-9]+ s:|^ i:'
A healthy server shows at least two entries (0 s: for its own certificate, 1 s: for the intermediate). If you see only 0 s:, the intermediate is missing.
Fix: configure the server with the full chain: its own certificate first, then the intermediate(s). The root is not needed.
cat api.example.com.crt issuing-ca.crt > fullchain.pem
- nginx:
ssl_certificate /etc/ssl/api.example.com/fullchain.pem;(with certbot, usefullchain.pem, notcert.pem). - Apache 2.4.8 and later: put the same file in
SSLCertificateFile. - IIS: import the intermediate into the computer’s “Intermediate Certification Authorities” store, or import a PFX that contains the chain, then rebind the site.
Reload the server and run the check again.
The root is a private CA that curl does not trust
Internal servers often use a company CA. Look at the issuer of the last certificate in the -showcerts output; if it is your company’s CA, test with its root:
curl --cacert corp-root.pem https://git.example.com/
If that works, add the root to the operating system’s store, which curl and many other tools use:
# Debian and Ubuntu (the file must end in .crt)
sudo cp corp-root.pem /usr/local/share/ca-certificates/corp-root.crt
sudo update-ca-certificates
# RHEL, Fedora and similar
sudo cp corp-root.pem /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract
# Windows (administrator): for curl.exe and git with Schannel
Import-Certificate -FilePath .\corp-root.cer -CertStoreLocation Cert:\LocalMachine\Root
For git only, scope the root to one host: git config --global http.https://git.example.com/.sslCAInfo /path/to/corp-root.pem. On Windows, git config --global http.sslBackend schannel makes git use the Windows store, where IT usually deploys company roots.
A TLS-inspection proxy re-signs your traffic
Some company proxies and antivirus products decrypt HTTPS and re-sign every site with their own root. Check who issued the certificate for a public site:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -issuer
If the issuer is your proxy or security product rather than a public CA, ask IT for the proxy’s root certificate and add it as above.
The CA bundle is missing or out of date
Minimal container images often have no CA bundle at all, and old systems lack newer roots. See which bundle curl uses:
curl -v https://example.com/ 2>&1 | grep -E 'CAfile|CApath'
Install or update the bundle: apt-get install ca-certificates on Debian and Ubuntu, apk add ca-certificates on Alpine.
Check yours
Paste the certificates from openssl s_client -showcerts (everything between the BEGIN and END CERTIFICATE lines). The checker shows which link of the chain is missing.
Learn why
- Certificate chains and path validation: why curl needs every link, and why browsers hide a missing intermediate.
- Who do we trust? CAs, trust stores and self-signed certificates: which store each tool reads.
- Related: verify error:num=20 and self-signed certificate in certificate chain.
- curl’s own guide: SSL Certificate Verification.
- See also: schannel: SEC_E_UNTRUSTED_ROOT (git and curl with Schannel on Windows) and curl: (77) (the CA file cannot be loaded).
Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.