A certificate on its own does not prove anything. It is trusted because it was signed by another certificate, which was signed by another, ending at a root the browser already trusts.
Your server has to send the middle of that chain. When it does not, the site works in your browser and fails somewhere else, which is the most confusing certificate problem there is.
The three parts
Your certificate (the leaf), naming your domain.
One or more intermediates, issued by the certificate authority and used to sign yours.
The root, which is already in every browser and operating system.
The client must be able to build a path from your certificate to a root it trusts. It has your certificate because you sent it, and it has the root already. The intermediates are the part that has to arrive from you.
Why the root is not sent
Sending the root is harmless and pointless: the client either already trusts it, in which case yours adds nothing, or it does not, in which case yours does not help.
Send the leaf and the intermediates. Nothing else.
What a missing intermediate looks like
This is the signature symptom, and recognising it saves hours.
The site works perfectly in your desktop browser. It fails on some phones, in some apps, and with curl.
The reason is that browsers cache intermediates. Once your browser has seen an intermediate on any site, it can complete the chain for yours without being sent it. So the developer's machine, which has visited thousands of sites, is the least likely to notice.
Some clients also fetch the missing intermediate themselves, which works and adds a delay to the connection. Others simply refuse.
So "it works for me" is not evidence here, and a report of "it fails on my phone" usually is.
Checking the chain
openssl s_client -connect example.com:443 -servername example.com
Read the top of the output. A complete chain shows your certificate followed by each intermediate, and ends with:
Verify return code: 0 (ok)
A chain with only one certificate listed, or a verify code mentioning "unable to get local issuer certificate", is a missing intermediate.
The -servername flag matters on shared hosting. Without it the server does not know which site you want and may return a different certificate entirely, which produces a mismatch that looks like a different fault.
Fixing it
The intermediate came with your certificate, usually called the CA bundle or chain file.
In cPanel's SSL/TLS interface there is a field for it alongside the certificate and the key. Paste it there and reinstall. cPanel often fills it in automatically, and that automatic value is occasionally wrong, check the chain afterwards rather than assuming. Installing an SSL certificate walks through the fields.
On a server you configure directly, the certificate file must contain the leaf followed by the intermediates, in that order. Order matters: leaf first, then each signer. A file with them reversed fails on strict clients and works on lenient ones, which reproduces the same confusing pattern.
Certificates issued automatically include the chain, so this problem belongs almost entirely to purchased certificates installed by hand.
Expired intermediates
Worth knowing because it produces an outage nobody caused.
Intermediates expire like anything else. A server holding an old chain file can serve an expired intermediate alongside a perfectly valid certificate, and clients reject the chain.
The site breaks on a day when nothing was changed. The fix is to update the chain file from the authority's current bundle rather than reissuing the certificate.
Cross-signing, briefly
Newer authorities are sometimes cross-signed by an older root, so that devices too old to know the new root can still build a path through the old one.
That is why a chain sometimes contains a certificate that looks redundant. Removing it to tidy things up breaks older devices while working fine on everything you test with.
Leave the chain exactly as the authority supplied it.
Test from outside, and on a real phone
Your own browser is the worst available test for this specific problem.
Use an online SSL checker, which reports the chain as an unprimed client sees it. Then check on a phone that has not visited your site.
Both take a minute and both catch what a desktop browser hides. The SSL troubleshooting guide walks through the other faults that look similar.
After any certificate change
Check the chain, not just the padlock. A renewal that installed the certificate without its new chain file is the common way a working site develops this fault.
And restart or reload the web server after installing. A certificate on disk that the running process has not read is still the old one, and that is a genuinely maddening ten minutes if you do not think of it.
To confirm whether the intermediates are actually being sent, ask the server directly. How to Check a Certificate from the Command Line explains the command and how to read it.
Where the trust at the top of that chain actually comes from explains several unrelated-looking failures. Why a Certificate Is Trusted and What a Root Store Is picks it up from there.
Read the chain the server actually sends
The certificate file and what the server transmits are different things, and only one of them matters to a visitor.
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null \ | grep -E '^(subject|issuer|s:|i:)' | head -20
Read it as a sequence. The first entry is your certificate. Each following entry should be the issuer of the one before it. When one is missing, the sequence breaks and the client has to find the gap itself.
A chain sent in the wrong order, or containing the root, is technically untidy and usually works. A chain with a missing link is the fault, and it produces a site that works in one browser and fails in another depending on what each has cached.
Build the file correctly
Most chain problems are created while assembling the file, and the order is not arbitrary.
cat certificate.crt intermediate.crt > fullchain.crt grep -c 'BEGIN CERTIFICATE' fullchain.crt openssl crl2pkcs7 -nocrl -certfile fullchain.crt | openssl pkcs7 -print_certs -noout | grep subject
The site certificate comes first, then each intermediate in order towards the root. The count tells you how many certificates ended up in the file, which catches the common mistake of pasting one over another rather than after it.
Do not append the root. It adds bytes to every connection and the client either has it already or does not trust it, so sending it changes nothing.
Test where the trust store is old
Your own machine has an up to date trust store and a cache of intermediates it has met before, which is exactly why it is the wrong place to test from.
echo | openssl s_client -connect example.com:443 -servername example.com -CApath /dev/null 2>&1 | grep 'Verify return' echo | openssl s_client -connect example.com:443 -servername example.com -verify_return_error 2>&1 | tail -3
Pointing at an empty trust store forces the connection to succeed or fail on what the server sent alone, with nothing supplied locally. That is the closest simple approximation of a device meeting your site for the first time.
Then check on a phone on mobile data rather than on the office network. Older devices and captive networks are where an incomplete chain shows first, and they are the visitors least likely to report it. SSL certificate troubleshooting deals with the other failures that look similar.