Skip to content
ecdsa.com
curl (OpenSSL backend)curl

Error message

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

Also printed as

  • curl: (60) SSL certificate OpenSSL verify result: unable to get local issuer certificate (20)
  • SSL certificate problem: unable to get local issuer certificate

OpenSSL-based curl (Linux, macOS Homebrew, Git for Windows) prints this text; newer releases (8.22.0 in our test, while 8.14.1 still used the text above) word it "SSL certificate OpenSSL verify result: unable to get local issuer certificate (20)", adding OpenSSL's verify code. curl built with Schannel on Windows reports the same situation as "schannel: SEC_E_UNTRUSTED_ROOT (0x80090325)", and GnuTLS-based libcurl (used by git on Debian and Ubuntu) as "server verification failed: certificate signer not trusted". Tools built on libcurl, such as PHP and git, print the part after "(60) " with their own prefix.

curl (OpenSSL backend) — CURLE_PEER_FAILED_VERIFICATION, X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (20)

What it means

curl received the server's certificates but could not find, in its own CA bundle, the certificate that issued the last one in the chain. The certificate may be perfectly valid; curl simply cannot connect it to any root it trusts, so it refuses the connection (exit code 60). The missing piece is either an intermediate the server forgot to send or a root CA your machine does not have.

Why it happens

How to fix it

  1. 1.

    See what the server actually sends

    List the certificates in the handshake. If only one appears (depth 0) and its issuer is an intermediate CA, the server's configuration is incomplete and must be fixed on the server. If the chain ends at a company or self-made root, the client is missing that root.

    bash
    # Which certificates does the server send, and who issued the last one?
    openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \
      | grep -E '^ *[0-9]+ s:|^ *i:'
    #  0 s:CN=api.example.com
    #    i:C=US, O=Example Trust Services, CN=Example TLS Issuing CA   <- leaf only: intermediate missing
  2. 2.

    Fix the server: serve the full chain

    Configure the web server with a bundle that contains the leaf followed by every intermediate. This fixes the error for every client at once, including mobile apps and API clients you do not control.

    bash
    # Leaf first, then each intermediate in order; the root itself is optional
    cat api.example.com.crt intermediate.crt > fullchain.pem
    
    # nginx:   ssl_certificate     /etc/nginx/tls/fullchain.pem;
    # Apache:  SSLCertificateFile  /etc/apache2/tls/fullchain.pem
    # certbot: use live/<name>/fullchain.pem, never cert.pem
  3. 3.

    Install a private root CA into the system store

    When the chain legitimately ends at a company or internal root, add that root to the operating system's bundle. Get the root from your IT or platform team, or from the CA itself; never from the certificate the failing connection presented, since that is exactly what you cannot yet trust.

    bash
    # Debian, Ubuntu, Alpine (the file must end in .crt)
    sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt
    sudo update-ca-certificates
    
    # RHEL, Fedora, Amazon Linux
    sudo cp corp-root-ca.pem /etc/pki/ca-trust/source/anchors/
    sudo update-ca-trust
  4. 4.

    Point curl at a bundle for one command or one shell

    --cacert adds trust for a single call without touching the system; CURL_CA_BUNDLE sets the bundle for every curl call in the environment. The file can hold several PEM certificates.

    bash
    curl --cacert corp-root-ca.pem https://api.example.com/
    
    # or for every curl command in this shell (a file may hold several PEM certificates)
    export CURL_CA_BUNDLE=/opt/certs/corp-and-public-roots.pem
  5. 5.

    Install the CA bundle in minimal containers

    If even public sites fail inside a container, the image has no bundle. Install ca-certificates in the image (or copy /etc/ssl/certs/ca-certificates.crt from a build stage) rather than switching verification off.

    bash
    # Debian/Ubuntu-based images
    apt-get update && apt-get install -y --no-install-recommends ca-certificates
    # Alpine
    apk add --no-cache ca-certificates
  6. 6.

    Temporary diagnostic: confirm it is a trust problem

    -k (--insecure) disables certificate verification, so a successful response only proves the network path works and the failure is about trust. Never leave it in scripts: with -k anyone on the path can read and alter the traffic, including credentials and downloaded code.

    bash
    # Diagnostic only: if this works, the problem is trust, not the network.
    curl -sS -k -o /dev/null -w '%{http_code}\n' https://api.example.com/

Related errors

Browse the full signature error database