curl: (60) SSL certificate problem: unable to get local issuer certificate. Confused, you open the exact same address in Google Chrome, Firefox, or Safari, and the page loads cleanly with a valid security padlock. It is easy to assume the issue is a bug in curl, a broken local CA bundle on your workstation, or an aggressive corporate firewall.
In almost every case where this happens, your server is misconfigured, but your browser is hiding the mistake from you. The web server is failing to send the complete certificate chain, and automated tools like curl are simply adhering strictly to standards where desktop browsers choose to be forgiving.
Why does a browser trust a certificate that curl rejects?
Why can't curl just download the missing intermediate like Chrome does?
requests, Go's net/http, and Node.js—are built with different operational constraints than desktop web browsers.
First, fetching missing certificates over the network introduces non-deterministic latency. In an automated script or a microservice architecture, an outbound HTTP call to download an intermediate certificate might block execution, trigger timeouts, or fail entirely if outbound network access is restricted by firewall egress rules.
Second, following AIA pointers introduces complexity and potential attack surfaces. Chasing an unauthenticated HTTP link to fetch an intermediate certificate requires an extra network stack loop during what should be an isolated TLS handshake. If the remote server hosting the intermediate file is slow, down, or compromised, the client's handshake hangs or encounters protocol issues. Curl relies on underlying cryptographic libraries like OpenSSL, GnuTLS, or LibreSSL, which traditionally treat the TLS handshake as an offline verification process against the server's provided data and the local root store.
Because curl does not maintain an opportunistic cache of certificates seen in previous sessions and does not perform AIA chasing by default, it evaluates only what your server presents during the handshake. If your server leaves out the intermediate link, curl fails immediately.
What actually happens during the TLS handshake when an intermediate is missing?
Certificate message to the client. RFC standards specify that the server should provide a list of certificates starting with the leaf and followed by any necessary intermediate certificates that lead to a root.
When the intermediate is omitted, the client receives only the leaf certificate. The client inspects the leaf certificate's "Issuer" field, which names the intermediate certificate authority that signed it. The client then checks its local trust store to see if that issuer is a known, pre-installed root certificate.
Because intermediate authorities are intentionally kept distinct from root stores for security and lifecycle management reasons, the lookup fails. The client cannot verify the digital signature on your leaf certificate because it does not possess the public key of the signer. Unable to link your certificate to a trusted root, the client aborts the connection to protect against potential man-in-the-middle attacks.
How do you spot a broken certificate chain using OpenSSL?
BASH
# Check the certificates sent by the server during the TLS handshake
openssl s_client -connect example.com:443 -servername example.comCertificate chain and Verify return code sections in the output. A broken setup usually produces an output structure like this:
TEXT
---
Certificate chain
0 s:CN = example.com
i:C = US, O = Let's Encrypt, CN = R3
---
...
Verify return code: 21 (unable to verify the first certificate)0, which is the leaf certificate (example.com). The issuer is listed as R3, but the server never sent the R3 intermediate certificate. Because the local trust store does not contain R3 as an anchor (it contains the parent root, such as ISRG Root X1), OpenSSL terminates the handshake with return code 21: unable to verify the first certificate.
On a correctly configured web server, the output displays multiple entries in the chain: entry 0 for the leaf certificate and entry 1 (or more) for the intermediate certificates. When the chain ends on a certificate signed by a root present in your local trust store, the final line reads Verify return code: 0 (ok).
How do you properly fix the certificate configuration on the server?
- The private key (
privkey.pemordomain.key). - The leaf certificate (
cert.pemordomain.crt). - The intermediate bundle (
chain.pemorca-bundle.crt). - A combined full-chain file (
fullchain.pem), which contains the leaf certificate followed directly by the intermediate certificate(s).
ssl_certificate directive must point to the combined file containing both the leaf and the intermediate:
TEXT
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;ssl_certificate to cert.pem instead of fullchain.pem, Nginx will only serve the leaf, causing the exact curl failure described here.
In Apache HTTP Server, the configuration depends on your Apache version. On Apache 2.4.8 and newer, the configuration mirrors Nginx: SSLCertificateFile should point to the full-chain file containing both your leaf and intermediate certificates. On older Apache versions, intermediate certificates had to be defined separately using the SSLCertificateChainFile directive, while SSLCertificateFile held only the domain certificate.
Once you update the configuration files and reload your web server, rerun the openssl s_client command or test with curl -vI https://example.com. Once the complete chain is served, curl will establish the connection without warnings.
Testing your sites with curl or OpenSSL should be a standard part of deploying any new certificate. A desktop browser's ability to render your site smoothly does not guarantee that your certificate deployment conforms to TLS specifications, and API clients or automated background workers will quickly expose an incomplete chain.