A missing intermediate certificate is one of the most confusing SSL problems, because the site often looks fine in your browser while it fails everywhere else. This guide explains why, how to confirm it in a minute, and how to fix it on common servers.
The symptom
The website opens normally in Chrome or Edge, but:
curlsaysSSL certificate problem: unable to get local issuer certificate.- Python reports
CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate. - Java fails with
PKIX path building failed. - Mobile apps, payment providers, webhooks or API clients can't connect.
- Some Android phones or older devices show a certificate warning.
Why it happens
Certificate authorities don't sign your certificate with their root key directly. They use an intermediate certificate, so the full chain is:
Your certificate → intermediate certificate → root certificate (built into devices)
Devices ship with the roots, but not the intermediates. Your server must send its intermediate together with its own certificate. If it only sends its own, the client can't link it to a trusted root.
Desktop browsers often hide the problem: they cache intermediates they've seen before, or download the missing one using a link inside the certificate. Most other software doesn't, so it fails.
Confirm it in a minute
Use our SSL checker: an incomplete chain shows as "Certificate chain complete and trusted: Fail" with the explanation.
Or with openssl, look at the verification result at the end of the output:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| grep "Verify return code"
Verify return code: 0 (ok): the chain is fine.Verify return code: 21 (unable to verify the first certificate): the intermediate is missing.
You can also count how many certificates the server sends; a correct setup sends at least two:
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
| grep -c "BEGIN CERTIFICATE"
The fix: serve the full chain
You need a full-chain file: your certificate first, followed by the intermediate certificate(s). Don't include the root.
- Let's Encrypt (Certbot) creates it for you:
fullchain.pem. - Commercial certificates come with a "CA bundle" or "intermediate" file. Combine them, your certificate first:
cat your_domain.crt ca_bundle.crt > fullchain.crt
Check the combined file before using it (openssl verify should print OK):
openssl verify -untrusted ca_bundle.crt your_domain.crt
Nginx
Point ssl_certificate at the full chain, not at your certificate alone:
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
Then sudo nginx -t && sudo systemctl reload nginx. More in How to install or renew SSL on Nginx.
Apache
On Apache 2.4.8 and later, SSLCertificateFile takes the full chain:
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
On older versions, keep your certificate in SSLCertificateFile and add the intermediate with SSLCertificateChainFile. Then sudo apachectl configtest && sudo systemctl reload apache2. More in How to install or renew SSL on Apache.
IIS (Windows)
IIS sends the intermediates it finds in the Windows certificate store. Open Manage computer certificates (certlm.msc), go to Intermediate Certification Authorities → Certificates, right-click All Tasks → Import and import your CA's intermediate certificate. Importing a .pfx that includes the chain also works. More in How to install or renew SSL on IIS.
Node.js
Load the full chain as the certificate:
https.createServer({ key: fs.readFileSync("privkey.pem"), cert: fs.readFileSync("fullchain.pem") }, app);
AWS load balancers and CloudFront
Certificates issued by AWS Certificate Manager always include their chain. For a certificate you import into ACM, paste the intermediate(s) into the Certificate chain field when importing; leaving it empty causes exactly this problem.
After fixing
Run the SSL checker again, or the openssl command above, and confirm Verify return code: 0 (ok). Fix it everywhere the certificate is installed: every server behind a load balancer, and the next renewal too, if you install certificates by hand.
CertHalo alerts you when a certificate stops being trusted, including when a renewal is installed without its intermediate. Monitor 3 certificates free.