• 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, use fullchain.pem, not cert.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

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 (everything between the BEGIN and END CERTIFICATE lines). The checker shows which link of the chain 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