2026年11月12日
2026年11月12日
Fix SSL Intermediate CA (Chain) Errors: Checks and Nginx/Apache Setup
This article is a localized edition of the Japanese original. Read the Japanese original
If your HTTPS site opens fine in a desktop browser but connections from mobile apps, curl, an external service's webhook or WordPress on another server fail with "unable to verify the certificate," the cause is usually that the server isn't sending the intermediate CA certificate. This article covers how to check whether it's missing, how to build a correct chain, how to configure Nginx, Apache and Certbot, and how to confirm the fix.
How intermediate CA certificates and chains work
Server certificates are usually issued by an intermediate CA rather than directly by a root CA. A connecting client trusts the certificate by following this sequence (the chain):
- Server certificate (the certificate for your domain)
- Intermediate CA certificate (the CA that issued 1; there may be more than one level)
- Root CA certificate (preinstalled in the OS or browser)
The server needs to send 1 and 2. The client uses its own copy of the root certificate in 3, so you don't need to send it. If the intermediate is missing, the client can't connect 1 to 3 and verification fails.
Desktop browsers rarely show the problem because some of them fetch missing intermediates automatically or reuse intermediates they've seen before. curl, PHP and apps often don't do this, which is where the error finally surfaces.
Step 1: Check whether the intermediate certificate is missing
Use openssl s_client to see which certificates the server actually sends. Without -servername, a server hosting several domains may return a different certificate.
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
Look at two places in the output:
- The
Certificate chainlist:0 s:should be your domain, with intermediate CAs from1 s:onward. If there's only0, no intermediate is being sent - The final
Verify return code:0 (ok)means everything is fine.21 (unable to verify the first certificate)and20 (unable to get local issuer certificate)are typical when the intermediate is missing
You can confirm the same problem with curl.
curl -sSI https://example.com/
# If the intermediate certificate is missing, you get an error like this
# curl: (60) SSL certificate problem: unable to get local issuer certificate
The WordPress HTTP API (wp_remote_get() and friends) also uses cURL internally, so fetching from or integrating with other sites can produce the same cURL error 60.
Step 2: Inspect the certificate file you have
Check what's in the file you're configuring. Even when one file holds several certificates, this command shows every certificate's subject (whose certificate it is) and issuer (who issued it).
# Number of certificates in the file
grep -c 'BEGIN CERTIFICATE' fullchain.pem
# Show the subject and issuer of every certificate in order
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout
A correct file meets these conditions:
- The first certificate is for your domain
- Each certificate's issuer matches the next certificate's subject
- It doesn't include a certificate whose subject and issuer are the same (a self-signed root)
A certificate whose subject and issuer differ is a "cross-signed" intermediate signed by another root, even if its name contains "Root." As of October 2026, Let's Encrypt uses chains in which the server certificate is followed by an intermediate CA and such cross-signed certificates. They are required intermediates, so don't remove them.
Step 3: Get the intermediate certificate
Intermediate certificates are distributed by the CA that issued your certificate (or the reseller). They're often included in the issuance email, the ZIP file you downloaded, or the "intermediate certificate" field of the control panel, so use that first.
If you can't find it, the server certificate may state where to get its issuer's certificate (CA Issuers).
# Check the CA Issuers URL in the server certificate's AIA extension (OpenSSL 1.1.1 or later)
openssl x509 -in server.crt -noout -ext authorityInfoAccess
# Download from the CA Issuers URL shown (replace the URL with the one from the output above)
curl -o intermediate.der http://ca.example.net/intermediate.crt
# If the downloaded file is DER, convert it to PEM
openssl x509 -inform DER -in intermediate.der -out intermediate.pem
Before using it, confirm that the downloaded file's subject matches the server certificate's issuer.
Step 4: Build the fullchain in the right order and verify it
Put the server certificate first, followed by the intermediate certificate. Don't include the root certificate.
# Concatenate in order: server certificate, then intermediate
cat server.crt intermediate.pem > fullchain.pem
# If the first file lacks a trailing newline, the boundary between certificates breaks. Make sure no line like this exists
grep -n 'END CERTIFICATE-----.*BEGIN' fullchain.pem
Before deploying, verify that the chain connects. If you see OK, it correctly reaches a root certificate held by the OS.
openssl verify -untrusted intermediate.pem server.crt
# server.crt: OK
Also confirm that the private key and server certificate are a pair. If the two outputs match, they belong together.
openssl x509 -in server.crt -noout -pubkey | openssl sha256
openssl pkey -in privkey.pem -pubout | openssl sha256
Make the private key file readable only by its owner (chmod 600 privkey.pem).
Step 5: Configure the web server
Nginx
Point ssl_certificate at the fullchain that combines the server certificate and the intermediate.
server {
listen 443 ssl;
server_name example.com;
# Server certificate + intermediate (one file, in this order)
ssl_certificate /etc/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/example.com/privkey.pem;
}
ssl_trusted_certificate specifies certificates used to verify OCSP responses and client certificates, and the certificates listed there are not sent to clients. It won't fix a missing intermediate, so fix the file in ssl_certificate instead.
sudo nginx -t
sudo systemctl reload nginx
Apache (2.4.8 or later)
From Apache 2.4.8, SSLCertificateFile can point to a file that includes the intermediate certificates. The previously used SSLCertificateChainFile became obsolete in 2.4.8.
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/ssl/example.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/example.com/privkey.pem
</VirtualHost>
sudo apachectl configtest
# Ubuntu / Debian
sudo systemctl reload apache2
# RHEL / Rocky Linux / AlmaLinux
sudo systemctl reload httpd
With Let’s Encrypt (Certbot)
Certbot creates these files in /etc/letsencrypt/live/<domain>/.
| File | Contents |
|---|---|
cert.pem |
Server certificate only |
chain.pem |
Intermediates only |
fullchain.pem |
Server certificate + intermediates |
privkey.pem |
Private key |
Use fullchain.pem as the web server's certificate. Pointing it at cert.pem leaves out the intermediate. Let's Encrypt's intermediates change from time to time, and as of October 2026 the official page lists the old "Let's Encrypt Authority X3" as expired. Rather than downloading an intermediate from an X3 URL in an old article and concatenating it, use the fullchain.pem that Certbot regenerates on every renewal as is.
Shared hosting, CDNs and load balancers
If the control panel has separate fields for "certificate," "private key" and "intermediate certificate (CA certificate)," paste the intermediate CA certificate into the intermediate field. Don't include the root. If HTTPS terminates at a CDN or load balancer, fix the certificate settings on that service rather than on the web server.
Step 6: Confirm after applying
After applying the change, run the Step 1 command again and confirm that intermediate CAs appear in Certificate chain and that you get Verify return code: 0 (ok). For an outside check, you can also use Qualys SSL Server Test (SSL Labs). In the results, "Chain issues: Incomplete" means an intermediate is missing, and "Contains anchor" means you've included the root certificate.
When you renew a certificate, rebuild the chain with the intermediate supplied with the new certificate. Even with the same CA, the issuing intermediate can change at renewal.
Summary
- If a site opens in desktop browsers but fails in
curl, apps or webhooks, suspect a missing intermediate certificate - Use
openssl s_client -servernameto check which certificates are sent and theVerify return code - The chain goes "server certificate → intermediate." Don't include the root
- Point Nginx's
ssl_certificateor, on Apache 2.4.8+,SSLCertificateFileat the fullchain. With Certbot, usefullchain.pem
Related articles:
- What to do when the intermediate certificate isn't installed
- How TLS certificate chains work
- Checking certificate details with OpenSSL
- Setting up an SSL certificate on Nginx with Certbot
- How to fix cURL errors in WordPress
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)