Command-line tools

curl: (60) SSL certificate problem: causes and fixes

By · Last verified

Error messagecurl: (60) SSL certificate problem: unable to get local issuer certificate

curl: (60) SSL certificate problem means curl reached the server but could not build a trusted chain from the server's certificate to a root CA it trusts. In most cases the server forgot to send its intermediate certificate; otherwise the client's CA bundle is missing, outdated, or does not contain your company's private root.

Quick fix

A chain with one certificate means the server must be fixed; a full chain means the client's trust store is the problem.

Find out which side is broken before changing anything. Test the server from a clean environment:

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
  • If the output shows only one certificate (depth=0) and ends with Verify return code: 21 (unable to verify the first certificate) or 20 (unable to get local issuer certificate), the server chain is incomplete. Install the full chain on the server (see missing intermediate certificate).
  • If the server sends a full chain and other machines connect fine, your client's trust store is the problem. Update it, or point curl at the right root:
curl --cacert /path/to/company-root.pem https://example.com
# or for every curl call in this shell
export CURL_CA_BUNDLE=/path/to/ca-bundle.pem

You can also paste the hostname into the SSL checker to see the chain the server sends.

Causes

1. The server does not send its intermediate certificate

A server that omits its intermediate certificate is the most common cause. Web servers must send the leaf certificate plus every intermediate; only the root is left out. Browsers often hide the mistake, curl does not.

  • Confirm: openssl s_client -showcerts lists a single certificate, and its issuer is an intermediate CA rather than a root.
  • Fix: in Nginx point ssl_certificate at fullchain.pem (leaf followed by intermediates), not cert.pem. In Apache 2.4.8+ put the full chain in SSLCertificateFile. Rebuild the chain with the chain builder if you only have the leaf.

2. The client CA bundle is missing or outdated

Minimal container images and old systems often ship without a CA bundle, or with one that predates a newer root.

  • Confirm: curl -v https://example.com 2>&1 | grep -i cafile shows a path that does not exist, or the server's root is not in that file.
  • Fix: install or update the system package (ca-certificates), or download Mozilla's bundle from curl.se and use it with --cacert.

3. A TLS-inspecting proxy or antivirus

Corporate proxies, security gateways and some antivirus products decrypt HTTPS and re-sign it with their own root. Machines that have that root in the OS store but not in curl's bundle fail with error 60.

  • Confirm: the issuer in openssl s_client output names your proxy vendor instead of a public CA.
  • Fix: get the proxy root from your IT team and add it to the system trust store (below) or pass it with --cacert.

4. Self-signed or private CA certificate

Internal services often use a self-signed certificate or an internal CA. The message then reads self-signed certificate or self-signed certificate in certificate chain.

5. Hostname or date problems

If the message mentions subject name does not match or certificate has expired, curl verified the chain but the name or the validity period is wrong. Fix the certificate's SAN list or renew it.

Fixes by environment

On every platform, fix the server's chain or add the missing root to the store that curl uses; never switch verification off.

Debian / Ubuntu

sudo apt-get install --reinstall ca-certificates
sudo cp company-root.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

RHEL / Rocky / Fedora

sudo cp company-root.pem /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust

Alpine and Docker images

RUN apk add --no-cache ca-certificates
COPY company-root.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates

On Debian-based images use apt-get install -y ca-certificates instead.

macOS: the system curl uses the macOS keychain. Add the root in Keychain Access (System keychain, "Always Trust"). A Homebrew curl uses its own bundle; check curl -V and the CAfile: line from curl -v.

Windows: curl.exe shipped with Windows 10/11 uses Schannel and the Windows certificate store, so import the root with certutil -addstore -f Root company-root.cer from an elevated prompt.

PHP, Git and other libcurl users inherit the same problem; see PHP cURL error 60 and Git SSL certificate problem.

Don't do this

  • curl -k / --insecure in scripts. It turns off every certificate check, including the hostname check. A man-in-the-middle can then read API tokens and change downloads.
  • echo insecure >> ~/.curlrc. This silently disables verification for every future curl call on that account.
  • Replacing the system bundle with a file copied from a forum. Use the OS package or the bundle published on curl.se, and add private roots alongside it rather than instead of it.

FAQ

What does curl exit code 60 mean?

Exit code 60 is CURLE_PEER_FAILED_VERIFICATION: the TLS handshake worked, but the server certificate or its chain could not be verified against curl's trusted CA certificates. The text after the colon (for example "unable to get local issuer certificate" or "self-signed certificate") is the underlying OpenSSL reason.

Why does the site open in Chrome but fail in curl?

Chrome can fetch a missing intermediate certificate on its own (AIA fetching) and may have it cached from another site. curl does neither, so a server that sends only the leaf certificate works in the browser and fails in curl. Check the chain with the chain builder and fix the server.

Is `curl -k` ever acceptable?

Only for a one-off manual test against a host you control, when you already know why verification fails. In scripts, CI jobs and cron tasks -k disables all certificate checks, so anyone on the network path can impersonate the server and read or change the data.

Where does curl look for CA certificates?

It depends on the TLS backend and how curl was built. OpenSSL builds use a compiled-in bundle path such as /etc/ssl/certs/ca-certificates.crt (Debian/Ubuntu) or /etc/pki/tls/certs/ca-bundle.crt (RHEL); Windows Schannel and macOS builds use the system store. Run curl -v https://example.com and read the CAfile: line.

Sources

  1. curl: SSL Certificate Verification
  2. libcurl error codes
  3. curl: CA certificates extracted from Mozilla
  4. curl man page