All articles
Application Security

TLS Certificate Chain Errors: Hostname, Expiry, Intermediates, and Trust

Debug SSL validation errors caused by missing intermediates, hostname mismatches, expiry, incorrect chains, or client trust-store differences.

Gloria Garcia·AppSec Engineer
·20 min read
Share:

Answer in brief

Diagnose TLS certificate errors in order: confirm the exact SNI hostname, check SAN coverage and validity dates, inspect the chain presented by the server, then test whether the affected client's trust store can build a path to an accepted root.

Use this guide as a diagnostic workflow, then verify the result against your production environment and the primary documentation below.

You deploy a new TLS certificate, open the site in a familiar browser, and see a secure connection. Ten minutes later, a Node.js service reports UNABLE_TO_VERIFY_LEAF_SIGNATURE, an Android client rejects the issuer, or a webhook sender refuses to connect. The certificate is installed, but the endpoint is not universally trustworthy.

The “works in my browser” result proves only that one client, with one trust store, cache, network path, SNI name, and path-building implementation, completed the handshake. It does not prove that the server presents every required intermediate or that older and programmatic clients can build the same trust path.

TL;DR

  • Validate the exact hostname clients use, including SNI and Subject Alternative Name coverage.
  • Check notBefore, notAfter, and the affected client's clock—not only the leaf certificate's apparent issue date.
  • The server should normally present leaf first, then required intermediates, and omit the root.
  • Do not rely on browsers or operating systems to download or cache missing intermediates.
  • Compare the affected runtime's trust store with the trust path your browser built.
  • In 2026, publicly trusted TLS certificate validity is capped at 200 days under the CA/Browser Forum schedule; automation is operationally essential even though ACME is not formally mandatory.

Inspect the public endpoint—not only the files on disk.

Use the CodeAva SSL/TLS Certificate Chain Checker to review the certificate presented by a public hostname, its name coverage, validity, and chain. This catches the common case where renewal created a correct file but the load balancer or web server still presents an older certificate.

Open the SSL/TLS Certificate Chain Checker

Start by classifying the TLS error

“SSL error” is not a diagnosis. Certificate validation performs several independent checks, and replacing the certificate blindly can leave the actual failure untouched.

Error familyWhat failedFirst check
Hostname mismatchThe requested identity is not covered by the certificate SAN.Exact URL hostname, SNI, SANs, wildcard depth
Expired or not yet validA certificate in the path is outside its validity window.Leaf and intermediate dates, client clock, active edge
Unable to verify issuer or leafThe client cannot build a path from leaf to a trusted anchor.Presented intermediates and client trust store
Self-signed or unknown authorityThe path ends at a root the client does not trust.Intended private CA, root distribution, runtime CA settings
Wrong certificateA default virtual host, old CDN edge, or load balancer serves another leaf.SNI, DNS destination, every edge/listener, deployment reload

The 2026 certificate-lifetime schedule

The outline is right that public certificate lifetimes are shrinking, but the wording needs precision. Under CA/Browser Forum Ballot SC081v3, the maximum validity of publicly trusted TLS subscriber certificates dropped from 398 days to 200 days for certificates issued on or after March 15, 2026. This is an ecosystem maximum, not a promise that every CA will issue a certificate for the full period.

Certificates issuedCA/B Forum maximumOperational impact
Before March 15, 2026398 daysPrevious public TLS limit
March 15, 2026–March 14, 2027200 daysCurrent 2026 ecosystem limit; some CAs use 199 days
March 15, 2027–March 14, 2029100 daysRenewal and deployment checks run more frequently
On or after March 15, 202947 daysManual lifecycle management becomes operationally untenable

DigiCert, for example, moved to a 199-day operational maximum in February 2026 to remain below the 200-day industry cap. That does not make 199 days the universal CA/Browser Forum number. Issuers can choose shorter periods, and services such as Let's Encrypt have long issued much shorter certificates.

ACME is not mandated as the only protocol by the lifetime ballot. The practical conclusion is still unavoidable: issuance, validation, deployment, reload, and monitoring need an automated lifecycle. Automation that only obtains a certificate but fails to install it on every endpoint is not complete.

Expiry monitoring must test what clients receive

Checking /etc/letsencrypt/live/example.com/cert.pem proves that a file exists. It does not prove that NGINX reloaded, a Kubernetes secret rolled out, or every CDN edge and load balancer listener serves that certificate.

Anatomy of a TLS certificate chain

Public-key infrastructure separates the certificate used by the service from the root key trusted by clients. A typical path has three roles.

1. Leaf or end-entity certificate

The leaf identifies the service, such as api.example.com. Its Subject Alternative Name extension contains the DNS or IP identities it covers, and its public key corresponds to the private key held by the endpoint. It is signed by an issuing CA, usually an intermediate.

2. Intermediate CA certificate

An intermediate bridges the leaf to a root trust anchor. Public CAs keep root private keys highly protected and issue from subordinate CA keys instead. There may be more than one intermediate, and cross-signing can provide alternative paths for different client populations.

3. Root trust anchor

A root is trusted because it is distributed through a browser, operating system, runtime, device, container image, enterprise policy, or application trust store. Cryptographic signatures do not make an arbitrary root trusted; local trust policy does.

Presented by the server:
  1. api.example.com          (leaf)
  2. Example Issuing CA R3    (intermediate)

Already trusted by the client:
  3. Example Root CA          (trust anchor)

Built path:
  leaf -> intermediate -> trusted root

What the server should send—and in what order

In a normal publicly trusted deployment, the server sends the leaf first, followed by the intermediate certificates needed to build toward a root. It normally does not send the self-signed root because clients already distribute roots according to their own trust programs. Sending the root adds bytes and does not cause a client to trust it.

The presented list and the path a client ultimately builds are related but not always identical. A client may select an alternative locally available intermediate or trust anchor. That flexibility is useful for compatibility, but it is also why two clients can disagree on the same endpoint.

Failure 1: the server sends only the leaf

This is the classic incomplete-chain failure. The server has the correct private key and leaf certificate, so some clients complete the handshake. A clean runtime cannot find the issuer certificate needed to continue the path and reports errors such as:

  • Node.js/OpenSSL: UNABLE_TO_VERIFY_LEAF_SIGNATURE, UNABLE_TO_GET_ISSUER_CERT_LOCALLY, or “unable to verify the first certificate”.
  • Go: x509: certificate signed by unknown authority.
  • Python: SSLCertVerificationError with a certificate verify failure.

Why a browser can appear to repair it

Certificates can contain Authority Information Access locations for issuer information. Some clients and platforms can retrieve or cache missing intermediates; others do not, or do so under different network and security policies. Browsers can also have an intermediate cached from a previous site visit. The outline's fixed Chrome/Safari-versus-Firefox rule is not reliable across platforms and versions.

Treat successful recovery as a client convenience, not a server configuration strategy. The endpoint should present the intermediates needed by its supported clients.

The fix: deploy the CA-provided chain bundle

With Certbot-style paths, cert.pem is the leaf while fullchain.pem contains the leaf followed by intermediate certificate(s). For NGINX, point ssl_certificate to the full chain:

server {
    listen 443 ssl;
    server_name api.example.com;

    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
}

NGINX documents the file order as primary certificate first, then intermediate certificates. On Apache 2.4.8 and later, SSLCertificateFile can load intermediates from the same certificate file; older configuration guides that rely on a separate SSLCertificateChainFile may be obsolete for current Apache.

Do not build a chain by downloading similarly named certificates from an unverified source. Use the chain supplied for the exact issuer and certificate, then test the affected client population.

Failure 2: the SAN does not match the hostname

Modern service-identity verification uses identities in the Subject Alternative Name extension. The old Common Name field is not the place to solve missing DNS coverage. If the client requests api.example.com, that identity must match an appropriate SAN.

Certificate SANs:
  DNS:example.com
  DNS:www.example.com

Request:
  https://api.example.com/   -> hostname mismatch

A wildcard is constrained to the complete left-most label. *.example.com can match api.example.com, but it does not match the apex example.com or a multi-label name such as v1.api.example.com. Add the apex and any deeper names explicitly or use separate certificates that match the deployment model.

Connecting to an IP address changes identity validation

Testing https://203.0.113.10 is not equivalent to testing https://api.example.com. The IP literal is a different reference identity, and a multi-tenant endpoint may also present its default certificate without the hostname in SNI. Use curl --resolve to pin an origin IP while preserving the URL hostname and TLS SNI:

curl --resolve api.example.com:443:203.0.113.10   -Iv https://api.example.com/

Failure 3: a certificate is expired or not yet valid

The leaf's notBefore and notAfter values define its validity window. A client clock outside that window causes failure. Check the clock on the affected host or container, but do not assume the leaf is the only possible expiry point: an intermediate in the built path can also be outside its validity window.

echo | openssl s_client   -connect api.example.com:443   -servername api.example.com 2>/dev/null   | openssl x509 -noout -subject -issuer -dates -serial

An expiry incident after a successful renewal usually means the new certificate was not deployed everywhere. Check each CDN hostname, load balancer listener, ingress controller, region, and origin. DNS or traffic steering can make one test repeatedly hit only a healthy edge.

Failure 4: the root is not trusted

A self-signed root can be cryptographically sound and still be untrusted by a client. For internal service-to-service TLS, use a managed private PKI or internal CA and distribute its root through a controlled trust-store process. Trust only the intended CA, scope it appropriately, and plan rotation and revocation.

Let's Encrypt's staging environment is useful for testing ACME workflows, but its certificates chain to intentionally untrusted staging roots. It is not an internal-CA replacement and should not be installed as a public production certificate.

Never solve trust by disabling verification

Options such as curl -k, rejectUnauthorized: false, and global environment switches that disable TLS verification are diagnostic shortcuts, not fixes. They remove server authentication and expose the connection to interception.

Private CA trust must reach the actual runtime

Installing a corporate root in a laptop's operating-system keychain does not guarantee that a Node.js process, Java runtime, container image, Python environment, or embedded device uses that store. Document which trust source each runtime uses and configure the CA through its supported mechanism. Recent Node.js releases, for example, expose explicit system-CA integration options; version behavior matters.

Failure 5: SNI or one edge presents the wrong certificate

Server Name Indication lets a client include the intended hostname during the TLS handshake so a shared IP can choose the correct certificate. If a diagnostic connects by IP or omits SNI, a server can present its default virtual-host certificate, creating a misleading hostname error.

openssl s_client   -connect api.example.com:443   -servername api.example.com   -showcerts </dev/null

Multi-region and CDN deployments add another failure mode: most edges serve the new certificate while one listener, region, or custom-domain binding still serves the old or default certificate. Test from more than one network and, where the platform allows it, target each origin or edge while preserving SNI.

Failure 6: the chain is ordered badly or chooses an incompatible path

The server's certificate list should start with the leaf, and each following certificate should certify the one before it. A wrong order, unrelated certificate, or duplicate is not a harmless bundle detail. Tolerant clients may reorder or ignore extras, while stricter clients fail or build an unexpected path.

Cross-signed intermediates make path selection more subtle. Two certificates can contain the same public key and similar names but lead to different roots. The newest-looking chain is not automatically the most compatible chain for older devices. Use the CA's supported chain guidance and test the actual minimum runtime and device versions you support.

Failure 7: the certificate and private key do not match

A renewed certificate can be copied beside the wrong private key, or a secret manager can update one half of the pair. Many servers refuse to load the configuration, leaving the old process and certificate active. Compare public-key fingerprints without printing the private key:

openssl x509 -in certificate.pem -pubkey -noout   | openssl pkey -pubin -outform DER   | openssl sha256

openssl pkey -in private-key.pem -pubout -outform DER   | openssl sha256

The two hashes should match. You can perform the same local check in the X.509, CSR & PEM Inspector without pasting private material into a remote endpoint. Keep private keys out of tickets, chat, logs, and public scanners.

Failure 8: server-auth and client-auth certificates are confused

Mutual TLS adds a second certificate-validation direction. The client validates the server certificate, and the server validates a client certificate. An error such as “certificate required” or “unknown ca” from the server can concern the client-auth chain rather than the public server certificate.

Check Extended Key Usage, intended identity, client certificate validity, the intermediates the client sends, and the CA bundle trusted by the server. Do not add a client-auth CA to a broad public trust store merely to make one mTLS integration work.

How to inspect a live TLS chain with OpenSSL

Use the exact public hostname and send SNI. The basic diagnostic command is:

openssl s_client   -connect api.example.com:443   -servername api.example.com   -showcerts   -verify_hostname api.example.com   -verify_return_error   </dev/null

Each option answers a different question:

  • -connect selects the network destination and port.
  • -servername sends SNI so the server selects the intended virtual host.
  • -showcerts prints the certificate list the server sent; it is not a verified chain by itself.
  • -verify_hostname checks the requested service identity.
  • -verify_return_error makes a verification problem fail the test instead of merely printing an error and continuing.

OpenSSL s_client can continue after verification errors

OpenSSL documents s_client as a diagnostic tool. Without -verify_return_error, it can continue the handshake after certificate verification errors. Do not treat a connected session or exit status from a loosely configured command as proof that the chain is trusted.

Read the verification result

Look for all of the following, not only Verify return code: 0:

  • The first presented certificate is the expected leaf.
  • The SAN contains the exact DNS name under test.
  • The leaf and each required intermediate are within their validity windows.
  • Each issuer and subject relationship forms the intended order.
  • The negotiated endpoint is the correct environment and edge.
  • The verification used the same relevant CA trust as the affected client.

Inspect a local certificate or bundle

openssl x509 -in leaf.pem -noout   -subject -issuer -serial -dates   -ext subjectAltName   -ext authorityInfoAccess

openssl crl2pkcs7 -nocrl -certfile fullchain.pem   | openssl pkcs7 -print_certs -noout

The first command inspects one certificate. The second is a convenient way to list certificates from a PEM bundle. A syntactically readable bundle can still contain the wrong intermediate, so compare issuers and validate the path.

Verify local files against a chosen trust anchor

openssl verify   -CAfile trusted-root.pem   -untrusted intermediates.pem   -verify_hostname api.example.com   leaf.pem

This deliberately tests one trust configuration. It does not prove that Android, Java, Node.js, Windows, macOS, or an embedded device carries the same root and path-building behavior.

Do not stop after OpenSSL succeeds

Test at the same abstraction level as the failing client. OpenSSL helps inspect the handshake, while application runtimes add their own CA bundle, proxy, SNI, hostname, protocol, and environment configuration.

# curl uses its configured TLS backend and trust store
curl -Iv https://api.example.com/

# Preserve hostname and SNI while targeting one origin IP
curl --resolve api.example.com:443:203.0.113.10   -Iv https://api.example.com/

For Node.js, capture error.code, error.message, runtime version, OpenSSL version, and CA configuration. Recent Node.js documentation points users toward supported system-CA integration when an enterprise root is installed locally, rather than unsafe verification bypasses.

Quick error-to-fix table

Error or symptomLikely causeFix to test first
UNABLE_TO_VERIFY_LEAF_SIGNATUREMissing issuer intermediate or wrong private trust configurationInspect presented chain; install full chain on server.
ERR_CERT_COMMON_NAME_INVALIDSAN does not match hostname, or wrong SNI certificateTest exact hostname with SNI and inspect SANs.
CERT_HAS_EXPIREDExpired leaf/intermediate, stale edge, or bad client clockInspect live endpoint dates and every serving edge.
SELF_SIGNED_CERT_IN_CHAINPrivate or unexpected root is not trusted by the runtimeConfigure the intended CA; do not disable verification.
Browser works, API failsCached intermediate or trust-store/path-building differenceTest from a clean runtime and inspect server-sent chain.
Only one region failsOld certificate or wrong binding on one edge/listenerProbe each origin/edge while preserving hostname and SNI.

Tool workflow: public endpoint, local PEM, then HTTP headers

Start with the SSL/TLS Certificate Chain Checker for the public hostname. This tests what an external connection receives rather than what the deployment directory contains. Remember that one external check still represents one network path and one trust implementation; multi-edge systems need broader probing.

Use the X.509, CSR & PEM Inspector and SSL Matcher to decode local certificates, CSRs, bundles, and keys, inspect SANs and expiration, and verify that the public key matches. Local inspection is especially useful before deploying sensitive private-key material.

After the TLS handshake succeeds, use the HTTP Headers Checker to inspect HSTS, redirects, caching, and other HTTP response headers. The headers tool fetches a public URL, but header inspection is not a substitute for viewing and validating the full certificate list.

Automate the entire lifecycle, not only renewal

Shorter validity makes hidden manual steps fail more often. A reliable certificate pipeline covers each stage:

  1. Renew or issue early enough to preserve a recovery window.
  2. Complete domain-control validation and CAA checks.
  3. Verify the issued SAN set and expected issuer.
  4. Match the certificate to the intended private key.
  5. Assemble the CA-provided intermediate chain in the correct order.
  6. Deploy atomically to every listener, region, ingress, and edge.
  7. Reload or roll the serving process and confirm success.
  8. Probe the public endpoint with SNI and hostname verification.
  9. Test at least the oldest supported client trust environment.
  10. Alert on renewal failure, deployment failure, and live expiry independently.

For Certbot-managed systems, test renewal configuration with certbot renew --dry-run. Use an ACME staging environment to test issuance flows without consuming production issuance capacity, while remembering that staging certificates are intentionally untrusted.

When certificate issuance fails before deployment

A chain error happens after a certificate is served, but renewal can fail earlier during DNS or HTTP validation. Check the ACME error before replacing files. CAA policy, stale DNS, an incorrect AAAA record, split authority, or a challenge path routed to the wrong origin can prevent issuance. Use the DNS Record Debugging guide when the failure occurs during domain validation or CAA lookup.

Do not wait for the expiry alert to test renewal

An expiry alarm proves time is running out; it does not prove that the ACME account, validation method, DNS credentials, CAA policy, deployment hook, and server reload still work. Exercise the renewal path continuously and alert on each stage.

Production checklist

  1. Test the exact public hostname and port with SNI.
  2. Confirm SAN coverage, including apex and wildcard boundaries.
  3. Check leaf and intermediate validity dates and the client clock.
  4. Verify the server sends leaf first and required intermediates next.
  5. Normally omit the self-signed root from the served chain.
  6. Compare certificate and private-key public fingerprints.
  7. Test every CDN edge, load balancer listener, region, and ingress path.
  8. Test the oldest supported device and runtime trust store.
  9. Keep private-CA roots scoped and distributed through supported mechanisms.
  10. Never make disabled verification a permanent workaround.
  11. Dry-run renewal and verify live deployment independently.
  12. Monitor the certificate actually presented to clients.

Automate—and verify what is actually served

A valid leaf certificate is only one link in TLS authentication. The hostname must match, every certificate in the chosen path must be valid, the server must present the required intermediates, and the client must trust the root. Fix the first failed layer instead of replacing certificates or weakening verification blindly.

The move to 200-day public certificates in 2026, 100 days in 2027, and 47 days in 2029 makes lifecycle automation a reliability requirement. But automation is only complete when an external probe confirms that every production endpoint presents the new, correctly ordered chain for the right SNI hostname.

Sources and further reading

#TLS certificate chain errors#SSL certificate validation error#unable to verify leaf signature#missing intermediate certificate#certificate hostname mismatch#certificate expired#untrusted root certificate#OpenSSL s_client#fullchain.pem#200-day TLS certificate

Frequently asked questions

More from Gloria Garcia

Found this useful?

Share:

Want to audit your own project?

These articles are written by the same engineers who built CodeAva\u2019s audit engine.