You installed an SSL certificate, restarted your web server, and pointed your browser at https:// — yet some visitors on mobile devices still see a security warning, and your API clients throw SSL: CERTIFICATE_VERIFY_FAILED. The most likely cause is an incomplete certificate chain. Understanding how certificate chains work is the fastest way to diagnose and permanently fix this class of SSL error.
What Is an SSL Certificate Chain?
An SSL (or TLS) certificate chain — also called the chain of trust — is an ordered sequence of digital certificates that connect your domain's server certificate to a root certificate authority (Root CA) that browsers and operating systems trust unconditionally.
A standard chain has three tiers:
- Root CA — The top-level certificate pre-installed in the trust store of every major OS and browser (e.g. DigiCert Global Root CA, ISRG Root X1). Root CAs never issue certificates to individual websites directly.
- Intermediate CA — A certificate signed by the Root CA. The intermediate CA is the entity that actually issues your server certificate. There may be one or two intermediate levels depending on the CA's architecture.
- Server Certificate — The certificate bound to your domain. It contains your public key, the domain names covered (SAN), and the validity period.
When a browser connects over HTTPS, it performs chain validation: it walks up the chain from your server certificate through each intermediate until it finds a root CA in its trust store. If any link is missing or the chain cannot be verified, the browser displays a security warning.
Why Root CAs Do Not Issue Certificates Directly
This design is a deliberate security decision. A Root CA's private key is extraordinarily valuable — if it were stolen or misused, every certificate ever issued by that CA worldwide would need to be revoked, and every device on the planet would need a trust store update. That would be catastrophic.
To mitigate this risk, root CA private keys are stored offline in Hardware Security Modules (HSMs) inside physically secured, air-gapped facilities. Root CAs only sign intermediate CA certificates, which are then used for day-to-day issuance. If an intermediate CA is compromised, the CA can revoke just that intermediate and issue a replacement, while the root remains untouched.
Additional benefits of the intermediate layer include:
- Separate intermediates can enforce different validation policies (DV, OV, EV)
- Narrower revocation scope limits the blast radius of any incident
- Cross-signing allows a newer root to be trusted on older devices via a legacy root
What Is an Incomplete Chain and Why Does It Happen?
An incomplete chain occurs when your web server sends only the server certificate during the TLS handshake, without including the intermediate certificate(s) that the browser needs to verify the chain up to a trusted root.
Common causes:
- Installing only the server certificate — uploading
certificate.crtto your server config without the correspondingca_bundle.crtorintermediate.crt - Migrating a server — copying
server.keyandserver.crtbut forgetting the CA bundle - Let's Encrypt misconfiguration — pointing the web server config at
cert.peminstead offullchain.pem - Automated renewal hook failure — the renew hook updates the certificate files but the web server config still references the old paths
- CA delivering incomplete bundles — some providers only email the server certificate without explicitly calling out the separate CA bundle file
Why Desktop Browsers Sometimes Hide the Problem
Modern desktop browsers aggressively cache intermediate certificates. When your browser visits any website that uses the same intermediate CA, it stores that intermediate locally. Later, when it visits your site with an incomplete chain, it can reconstruct the chain from its cache — and shows a green padlock. This creates a deceptive situation: the server admin sees green on their laptop but mobile users on fresh devices (with empty caches) see a warning. This is why testing from a known-clean environment is essential.
Diagnosing the Chain with OpenSSL
OpenSSL is the most reliable tool for checking what your server is actually sending:
Step 1 — Check the number of certificates sent
openssl s_client -connect yourdomain.com:443 -showcerts 2>&1 | grep -c "BEGIN CERTIFICATE"
A result of 1 means incomplete chain. A result of 2 or 3 means the intermediate(s) are being sent correctly.
Step 2 — Verify each depth level
openssl s_client -connect yourdomain.com:443 -showcerts 2>&1 | grep -E "depth|verify"
Healthy output:
depth=2 C = US, O = DigiCert Inc, CN = DigiCert Global Root CA verify return:1 depth=1 C = US, O = DigiCert Inc, CN = RapidSSL TLS RSA CA G1 verify return:1 depth=0 CN = yourdomain.com verify return:1
Step 3 — Read the certificate details
openssl x509 -in /path/to/certificate.crt -text -noout | grep -E "Issuer|Subject|Not After|DNS:"
Step 4 — Confirm private key matches certificate
# Both md5 values must be identical openssl x509 -noout -modulus -in certificate.crt | openssl md5 openssl rsa -noout -modulus -in private.key | openssl md5
| Chain State | OpenSSL Signal | Client Impact | Fix |
|---|---|---|---|
| Complete chain | depth=2, verify OK at all levels | Green padlock everywhere | None needed |
| Incomplete chain | Only depth=0; intermediate missing | Mobile & API errors, browser warning | Add CA bundle / intermediate |
| Wrong order | Chain present but in wrong sequence | Intermittent client errors | Reorder: server → intermediate → root |
| Expired intermediate | verify error: certificate has expired | All clients fail | Update CA bundle from provider |
Fixing an Incomplete Chain on Apache
The recommended approach for Apache 2.4.8+ is to create a single fullchain.pem that includes both your server certificate and the intermediate, in that order:
Method 1 — Single fullchain.pem (recommended)
# Server certificate must come FIRST
cat certificate.crt intermediate.crt > /etc/ssl/certs/yourdomain.fullchain.pem
# In your VirtualHost block
<VirtualHost *:443>
ServerName yourdomain.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/yourdomain.fullchain.pem
SSLCertificateKeyFile /etc/ssl/private/yourdomain.key
</VirtualHost>
Method 2 — SSLCertificateChainFile (Apache < 2.4.8 only)
<VirtualHost *:443>
SSLEngine on
SSLCertificateFile /etc/ssl/certs/certificate.crt
SSLCertificateKeyFile /etc/ssl/private/private.key
SSLCertificateChainFile /etc/ssl/certs/ca_bundle.crt
</VirtualHost>
Always test before reloading:
apachectl configtest # must return "Syntax OK" systemctl reload apache2
Fixing an Incomplete Chain on Nginx
Nginx requires a single file combining the server certificate and all intermediates. The order matters — server certificate first, then intermediates in descending order:
# Build fullchain.pem
cat yourdomain.crt intermediate.crt > /etc/nginx/ssl/yourdomain.fullchain.pem
# Server block
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/yourdomain.fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/yourdomain.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
}
nginx -t # must show "syntax is ok" and "test is successful" systemctl reload nginx
Let's Encrypt — Using the Right File
Certbot creates four files inside /etc/letsencrypt/live/yourdomain/. The single most common misconfiguration is using the wrong one:
cert.pem— server certificate only (never use this in the web server config)chain.pem— intermediate certificate onlyfullchain.pem— cert.pem + chain.pem combined (always use this)privkey.pem— private key
# Correct Nginx config for Let's Encrypt ssl_certificate /etc/letsencrypt/live/yourdomain/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain/privkey.pem; # WRONG — cert.pem is missing the intermediate ssl_certificate /etc/letsencrypt/live/yourdomain/cert.pem;
Verify auto-renewal is working:
certbot renew --dry-run
Installing via DirectAdmin (Shared Hosting)
On AsiaGB shared hosting (powered by DirectAdmin), you manage SSL through the control panel without touching config files:
- Log in to DirectAdmin and navigate to SSL Certificates
- Choose Paste a pre-generated certificate and key
- Paste your Server Certificate in the Certificate field
- Paste your Private Key in the Private Key field
- Enable Use a CA Cert and paste the entire contents of
ca_bundle.crtorintermediate.crtin the CA Certificate field - Click Save
If the provider gave you a .zip file, extract it and look for a file named *ca-bundle*, *intermediate*, or *chain*. That is the file to paste in the CA Certificate field.
Pro tip: After updating your certificate chain, always test from a device or machine that has never visited your site before — or use a private/incognito window on a different network. Desktop browsers cache intermediates aggressively, so testing on your own machine may show a valid padlock even when the chain is still broken for everyone else. Running openssl s_client -connect yourdomain.com:443 -showcerts 2>&1 | grep -c "BEGIN CERT" from a server gives a definitive, cache-free answer.
OCSP Stapling: Chain Quality Affects TLS Performance
A correctly assembled certificate chain is a prerequisite for enabling OCSP Stapling. OCSP Stapling lets your server pre-fetch a signed revocation status response from the CA and attach it to the TLS handshake, eliminating the need for the client to make a separate OCSP request. This reduces latency on the first connection and improves user privacy by preventing the CA from learning which sites visitors are connecting to.
# Enable OCSP Stapling on Nginx ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/nginx/ssl/yourdomain.fullchain.pem; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s;
# Enable OCSP Stapling on Apache SSLUseStapling On SSLStaplingCache shmcb:/tmp/stapling_cache(128000)
Without a complete chain, the server cannot determine the OCSP responder URL embedded in the intermediate certificate, causing stapling to silently fail.
Frequently Asked Questions
What is an SSL certificate chain and why is an intermediate CA required?
An SSL certificate chain is a sequence of digital certificates that links your server certificate through one or more intermediate CAs to a root CA that browsers trust. Intermediate CAs are required because root CAs never issue certificates directly to websites — doing so would expose the root's private key. If an intermediate CA is compromised, only that intermediate needs to be revoked, leaving the root CA safe.
How do I check whether my certificate chain is complete?
Run openssl s_client -connect yourdomain.com:443 -showcerts and count the BEGIN CERTIFICATE blocks. A complete chain shows 2–3 certificates. You can also grep for verify return:1 at each depth level. If you only see depth=0, the intermediate is missing and clients that have not cached it will show a security warning.
How do I fix an incomplete chain on Apache and Nginx?
On Apache, concatenate your server certificate and intermediate into a single fullchain.pem file (server cert first), then point SSLCertificateFile at that file. Alternatively use SSLCertificateChainFile for Apache versions below 2.4.8. On Nginx, ssl_certificate must reference a fullchain.pem that already includes the intermediate. After editing, always run apachectl configtest or nginx -t before reloading the service.
Does Let's Encrypt have certificate chain issues?
Let's Encrypt provides a fullchain.pem that already bundles the server certificate and intermediate together. The most common mistake is using cert.pem (server cert only) instead of fullchain.pem in the web server configuration. Always reference fullchain.pem for ssl_certificate on Nginx or SSLCertificateFile on Apache. Certbot's auto-renew preserves these paths automatically.
All SSL Certificate Types at AsiaGB
DV, OV, EV, and Wildcard SSL starting from 1,000 THB/year with Installation Support included
View SSL Certificates