SSL/TLS error reference
Paste the exact error text into search, find the page, and you get the cause, a visitor-side check and the server-side fix.
Each page here explains one exact error string, such as NET::ERR_CERT_DATE_INVALID or SEC_ERROR_UNKNOWN_ISSUER: what it means, whether the site or your device is at fault, and the fix. Search for the text you see and work through the matching page from top to bottom.
How to use these pages
Start by testing the site, not your computer. Enter the domain into the SSL checker, or run this from any terminal:
curl ssl.sh/example.com
The checker connects from our servers with an up-to-date trust store and reports the certificate, chain, protocol versions and common misconfigurations. That single result splits every problem into two paths:
- The check fails too. The site is misconfigured: an expired certificate, a missing intermediate, a hostname mismatch or an old TLS setup. If you run the site, follow the "How site owners fix it" section of the error page. If you are only a visitor, there is nothing safe to fix on your side; tell the site owner and wait.
- The check passes, but your browser still complains. The cause is on your device or network: the clock, antivirus HTTPS scanning, a company proxy, a Wi-Fi login page, or an operating system too old to trust current root certificates. The "Is it the site or your device?" section on each page tells you what to check first.
What each page contains
Each error page lists the causes in order of likelihood, shows how the same failure looks in Chrome, Firefox, Safari, Edge and on phones, and gives copy-paste server configuration for Nginx, Apache and common panels. Related tools such as the chain builder, certificate decoder and TLS config generator are linked where they shorten the fix.
We never recommend clicking through a certificate warning on a site you do not control: it removes the only protection against someone intercepting the connection.
Browser errors
Your connection is not private
Chrome and Edge show "Your connection is not private" when a site's certificate fails a check. The NET:: code underneath tells you exactly why.
NET::ERR_CERT_DATE_INVALID
Chrome shows NET::ERR_CERT_DATE_INVALID when a certificate is expired or not yet valid. Check the site first, then your clock, then renew.
NET::ERR_CERT_AUTHORITY_INVALID
NET::ERR_CERT_AUTHORITY_INVALID means Chrome does not trust the certificate's issuer: self-signed, private CA, missing chain or interception.
NET::ERR_CERT_COMMON_NAME_INVALID
NET::ERR_CERT_COMMON_NAME_INVALID means the certificate does not cover the hostname you visited. Usually a missing www, subdomain or SNI setup.
ERR_SSL_PROTOCOL_ERROR
ERR_SSL_PROTOCOL_ERROR means the TLS handshake broke before a certificate could be checked. Usually a server or port misconfiguration, sometimes a proxy.
ERR_SSL_VERSION_OR_CIPHER_MISMATCH
ERR_SSL_VERSION_OR_CIPHER_MISMATCH means browser and server share no TLS version or cipher. Usually TLS 1.0-only servers or a CDN with no cert.
NET::ERR_CERT_REVOKED
NET::ERR_CERT_REVOKED means the CA has cancelled the site's certificate. Visitors should not continue; site owners must issue and install a new one.
Warning: Potential Security Risk Ahead
Firefox shows "Warning: Potential Security Risk Ahead" when a certificate check fails. The error code under Advanced tells you the cause and fix.
SEC_ERROR_UNKNOWN_ISSUER
SEC_ERROR_UNKNOWN_ISSUER means Firefox cannot trace the certificate to a trusted root: missing intermediate, private CA or HTTPS interception.
SSL_ERROR_BAD_CERT_DOMAIN
Firefox shows SSL_ERROR_BAD_CERT_DOMAIN when the certificate does not list the hostname you visited. Fix the SAN list, SNI or CDN binding.
SEC_ERROR_EXPIRED_CERTIFICATE
Firefox shows SEC_ERROR_EXPIRED_CERTIFICATE when a certificate is past its expiry date or your clock is ahead. How to tell which, and how to fix it.
MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
Firefox shows MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT when a certificate signed itself. Fine for your own router; never acceptable on a public site.
Missing intermediate certificate
An incomplete certificate chain works in Chrome but fails in curl, Android, Java and Python. How to detect a missing intermediate and serve the full chain.
Mixed content warning
"Mixed Content: the page was loaded over HTTPS, but requested an insecure resource" means an HTTPS page loads HTTP files. How to find and fix them.
NET::ERR_CERT_INVALID
NET::ERR_CERT_INVALID means Chrome found the certificate itself broken: bad encoding, forbidden extensions or rule violations. Common with homemade certs.
NET::ERR_SSL_OBSOLETE_VERSION
NET::ERR_SSL_OBSOLETE_VERSION means the server only offered TLS 1.0 or 1.1. Browsers have removed both; the server must enable TLS 1.2 or 1.3.
SSL_ERROR_UNSUPPORTED_VERSION
Firefox shows SSL_ERROR_UNSUPPORTED_VERSION when a site does not support TLS 1.2 or later. Why re-enabling TLS 1.0 is risky and how servers fix it.
ERR_SSL_UNRECOGNIZED_NAME_ALERT
ERR_SSL_UNRECOGNIZED_NAME_ALERT means the server rejected the hostname sent via SNI. Fix DNS, the virtual host, CDN binding or ssl_reject_handshake.
SSL_ERROR_NO_CYPHER_OVERLAP
SSL_ERROR_NO_CYPHER_OVERLAP means Firefox and the server share no cipher suite. Usually an outdated server cipher list or a changed Firefox setting.
PR_END_OF_FILE_ERROR
PR_END_OF_FILE_ERROR means the connection was cut during the TLS handshake. Usually a proxy, security software, changed TLS settings or the server.
SSL_ERROR_RX_RECORD_TOO_LONG
Firefox shows SSL_ERROR_RX_RECORD_TOO_LONG when the server answers HTTPS with plain HTTP. Usually listen 443 without ssl, or HTTPS sent to an HTTP port.
SEC_ERROR_INADEQUATE_KEY_USAGE
SEC_ERROR_INADEQUATE_KEY_USAGE means a certificate's key usage does not allow the operation. Common with homemade certs, device certs and stale Firefox data.
SEC_ERROR_OCSP_* errors
Firefox OCSP errors such as SEC_ERROR_OCSP_INVALID_SIGNING_CERT and SEC_ERROR_OCSP_OLD_RESPONSE mean a revocation response was unusable. Causes and fixes.
REQUIRED_TLS_FEATURE_MISSING
Firefox shows MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING when a Must-Staple certificate arrives without a stapled OCSP response. How to fix it.
Cannot visit: website uses HSTS
Chrome blocks certificate errors on HSTS sites with no bypass. Why it happens, what visitors can safely check, and how owners and developers fix it.
NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED
NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED means the certificate lacks valid Certificate Transparency SCTs required by Chrome. Causes and fixes.
ERR_SSL_KEY_USAGE_INCOMPATIBLE
ERR_SSL_KEY_USAGE_INCOMPATIBLE means an RSA certificate from a private CA lacks the key usage Chrome requires. Reissue it with digitalSignature.
Only Firefox: SEC_ERROR_UNKNOWN_ISSUER
Why a site opens in Chrome or Edge but Firefox reports SEC_ERROR_UNKNOWN_ISSUER: separate root stores, enterprise roots, antivirus and private CAs.
DLG_FLAGS_SEC_CERT_CN_INVALID
DLG_FLAGS_SEC_CERT_CN_INVALID is the IE and legacy Edge code for a certificate issued to a different hostname. Where it still appears and how to fix it.
DLG_FLAGS_INVALID_CA
DLG_FLAGS_INVALID_CA means Windows does not trust the certificate's issuer: self-signed, internal CA, missing intermediate or outdated root store.
NET::ERR_CERT_WEAK_SIGNATURE_ALGORITHM
NET::ERR_CERT_WEAK_SIGNATURE_ALGORITHM means the certificate or an intermediate is signed with SHA-1 or MD5. Reissue it with SHA-256 or better.
MOZILLA_PKIX_ERROR_MITM_DETECTED
Firefox shows MOZILLA_PKIX_ERROR_MITM_DETECTED when antivirus, a proxy or malware intercepts HTTPS with an untrusted root. How to identify and fix it.
ERR_BAD_SSL_CLIENT_AUTH_CERT
ERR_BAD_SSL_CLIENT_AUTH_CERT means a site requiring client certificates (mTLS) rejected yours or got none. Causes for users and server fixes.
SEC_ERROR_REUSED_ISSUER_AND_SERIAL
Firefox shows SEC_ERROR_REUSED_ISSUER_AND_SERIAL when two certificates share an issuer and serial number, typically a router or NAS that regenerated one.
Chrome "Not secure"
Chrome labels a page Not secure when it loads over plain HTTP or after a certificate warning was bypassed. How to tell which and how to remove it.
SSL_ERROR_HANDSHAKE_FAILURE_ALERT
Firefox shows SSL_ERROR_HANDSHAKE_FAILURE_ALERT when the server aborts the TLS handshake. Usually a required client certificate or no common cipher suite.
MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE
Firefox shows this error when a certificate's start date is still in the future by your clock. Usually the device clock is behind.
Phones and operating systems
Safari: This Connection Is Not Private
Safari shows "This Connection Is Not Private" when a certificate check fails. How to see the real reason on iPhone, iPad and Mac, and how to fix it.
Safari can't establish a secure connection
Safari can't establish a secure connection when the TLS handshake fails: old TLS on the server, content filters, VPN or DNS profiles, or Screen Time limits.
iOS: Cannot Verify Server Identity
iOS shows "Cannot Verify Server Identity" when a mail or web server's certificate fails a check. Common causes: wrong server name, self-signed or expired cert.
NSURLErrorDomain -1200
NSURLErrorDomain Code=-1200 means an iOS or macOS app's TLS handshake failed, often due to App Transport Security rules. How to diagnose and fix it.
NSURLErrorDomain -1202
NSURLErrorDomain -1202 means an iOS or macOS app rejected the server's certificate: untrusted root, wrong hostname, missing intermediate or Apple policy.
Android: user CA not trusted by apps
Since Android 7, apps ignore CA certificates installed by the user. Why company or debugging CAs work in Chrome but not apps, and how to configure trust.
Trust anchor for certification path not found
Android apps throw "Trust anchor for certification path not found" when the server chain can't reach a trusted root. Usually a missing intermediate.
Android "Network may be monitored"
Android shows Network may be monitored when an added CA certificate could intercept HTTPS. How to find who installed it and how to remove it.
Old Android and Let's Encrypt
Android versions before 7.1.1 do not trust ISRG Root X1, so Let's Encrypt sites fail on them since the cross-sign ended in 2024. Options for users and owners.
Flutter CERTIFICATE_VERIFY_FAILED
Flutter throws CERTIFICATE_VERIFY_FAILED when dart:io cannot verify the server certificate. Read the reason after the colon, then fix the chain.
macOS: certificate is not trusted
Keychain Access says "This certificate is not trusted" or "signed by an unknown authority". Why it happens on a Mac and how to fix it safely.
Windows Schannel errors
Decode Windows Schannel errors: events 36887/36888/36874/36871 and codes like SEC_E_UNTRUSTED_ROOT 0x80090325 and CRYPT_E_NO_REVOCATION_CHECK.
Wrong system clock
A wrong date or time makes every HTTPS site fail with certificate errors. How to fix the clock on Windows, macOS, iPhone and Android, and why it happens.
Antivirus HTTPS scanning
Antivirus web protection that scans HTTPS re-signs certificates and can cause SSL errors on every site. How to confirm it and fix it without losing protection.
Public Wi-Fi login (captive portal)
Public Wi-Fi login pages intercept HTTPS and cause certificate errors until you sign in. How to reach the login page safely on laptops and phones.
Corporate SSL inspection
Company firewalls and secure web gateways decrypt HTTPS and re-sign it. When their root is missing or apps pin certificates, users see SSL errors.
Servers and proxies
SSL handshake failed (overview)
SSL handshake failed is a generic message. Map it to the exact error your browser, app, CDN or server reports, find the failing step and fix it.
Nginx: key values mismatch
Nginx fails with key values mismatch when the private key does not belong to the first certificate in ssl_certificate. Compare the public keys and fix the file.
Nginx: SSL_CTX_use_PrivateKey failed
Nginx cannot load the private key: a mismatched key, wrong format, passphrase or file permissions. The OpenSSL reason at the end of the line names the cause.
Nginx: no ssl_certificate is defined
Nginx logs this when a TLS handshake reaches a port 443 server block without a certificate, often the default server. Add a certificate or reject the handshake.
Apache AH02572: failed to configure certificate
Apache stops with AH02572 when mod_ssl cannot load a matching certificate and key. The log line above it names the cause: key mismatch, format or path.
Apache AH01909
AH01909 is an Apache startup warning: the vhost's ServerName is not in its certificate's SAN or CN. Usually a missing ServerName or wrong certificate.
unsupported certificate purpose (clientAuth EKU removed)
Public TLS certificates no longer carry the clientAuth EKU, so mTLS with a Let's Encrypt or other public cert fails. Why, and how to move to a private CA.
IIS: logon session does not exist
IIS shows this error (0x80070520) when the certificate's private key is missing or inaccessible. How to check the key, re-import the PFX and rebind.
nginx "ssl_stapling" ignored
nginx warns "ssl_stapling" ignored when it cannot staple OCSP. Let's Encrypt certificates have had no OCSP URL since 2025, so this is expected.
Programming languages
Python CERTIFICATE_VERIFY_FAILED
Python raises CERTIFICATE_VERIFY_FAILED when it cannot verify a server certificate. Fix the server chain or give Python the right CA bundle, not verify=False.
PKIX path building failed
PKIX path building failed means the JVM could not chain the server certificate to a root in its truststore. Fix the server chain or import the right root.
Node.js: unable to verify the first certificate
"unable to verify the first certificate" in Node.js usually means the server sends no intermediate certificate. Fix the server chain or use NODE_EXTRA_CA_CERTS.
x509: certificate signed by unknown authority
Go programs print x509: certificate signed by unknown authority when no trusted root matches the server chain. Add the root to the system store or RootCAs.
self-signed certificate in certificate chain
This OpenSSL error means the chain ends in a root your client does not trust, usually a corporate proxy or internal CA. Trust that root; keep checks on.
Python SSL WRONG_VERSION_NUMBER
WRONG_VERSION_NUMBER means Python spoke TLS to something that answered in plain text: an https:// proxy URL, a wrong port, or a server without TLS on that port.
Python SSLEOFError: EOF occurred
SSLEOFError means the TLS connection was cut off mid-handshake or closed without close_notify. Check proxies, SNI, TLS versions and server limits.
SSLHandshakeException: handshake_failure
Received fatal alert: handshake_failure means client and server could not agree on TLS parameters. Check protocols, cipher suites, SNI and client certificates.
No subject alternative names present
Java throws No subject alternative names present when you connect by IP and the certificate has no SAN. Connect by hostname or reissue with an IP SAN.
Node.js: self-signed certificate
Node.js says "self-signed certificate" (DEPTH_ZERO_SELF_SIGNED_CERT) when the server's own certificate is self-signed. Trust a local CA; keep checks on.
Node.js: certificate has expired
Node.js reports CERT_HAS_EXPIRED when a certificate in the chain is past its notAfter date or the client clock is wrong. Check the server, chain and clock.
x509: relies on legacy Common Name field
Go 1.15+ rejects certificates that name the host only in the Common Name. Reissue the certificate with Subject Alternative Names; GODEBUG no longer helps.
.NET remote certificate is invalid
.NET throws this AuthenticationException when SslStream rejects a server certificate. Read the SslPolicyErrors value, then fix the chain, name or trust store.
PHP cURL error 60
PHP cURL error 60 means libcurl could not verify the server certificate, usually because PHP has no CA bundle. Set curl.cainfo; keep verification on.
Ruby certificate verify failed
Ruby's "certificate verify failed" means OpenSSL could not verify the server chain. Point Ruby at a valid CA bundle or fix the chain; avoid VERIFY_NONE.
requests SSLError: Max retries exceeded
requests raises SSLError: Max retries exceeded when the TLS handshake fails. Read the Caused by part and fix the chain, CA bundle or proxy.
UNSAFE_LEGACY_RENEGOTIATION_DISABLED
OpenSSL 3 refuses servers without RFC 5746 secure renegotiation. Fix the server, or allow legacy connect for one host with verification on.
urllib3 InsecureRequestWarning
urllib3 prints InsecureRequestWarning when an HTTPS request skips certificate checks. Find the verify=False and fix the trust problem behind it.
UNABLE_TO_GET_ISSUER_CERT_LOCALLY
Node.js cannot find the root that issued the server's chain in its bundled CA store. Add it with NODE_EXTRA_CA_CERTS or --use-system-ca.
ERR_TLS_CERT_ALTNAME_INVALID
ERR_TLS_CERT_ALTNAME_INVALID means the certificate does not list the hostname or IP you connected to. Fix the name, SNI or certificate.
Java No appropriate protocol
Java throws No appropriate protocol when every enabled TLS version is disabled or lacks a usable cipher suite, usually code pinned to TLSv1.
Keystore was tampered with
Java could not verify a JKS keystore's integrity: usually a wrong store password, sometimes a file corrupted by Maven filtering or a botched copy.
.NET SSL connection could not be established
HttpClient wraps every TLS failure in this message. Read the inner AuthenticationException, then fix the trust, name, protocol or network issue.
x509: certificate is valid for X, not Y
Go's crypto/x509 says the certificate's SANs do not include the host you dialed. Fix the name, ServerName or default certificate instead.
rustls invalid peer certificate: UnknownIssuer
rustls could not chain the server certificate to a root in its root store. Choose the right root source or add the missing root.
Command-line tools
curl: (60) SSL certificate problem
curl error 60 means curl could not verify the server's certificate chain. Fix the server chain or point curl at the right CA bundle instead of using -k.
Git: SSL certificate problem
Git's "SSL certificate problem: unable to get local issuer certificate" means libcurl could not verify the server. Fix the chain or set http.sslCAInfo.
npm: unable to get local issuer certificate
npm "unable to get local issuer certificate" means Node.js does not trust the root in front of the registry, usually a proxy or private registry. Use cafile.
pip SSL CERTIFICATE_VERIFY_FAILED
pip fails with CERTIFICATE_VERIFY_FAILED when it cannot verify PyPI or your index. Upgrade pip, use --cert or PIP_CERT, and avoid --trusted-host.
curl: (35) SSL connect error
curl error 35 means the TLS handshake failed: protocol or cipher mismatch, a reset connection, plain HTTP on a TLS port, or a required client certificate.
curl: (77) certificate verify locations
curl error 77 means curl could not read its CA bundle: a missing ca-certificates package, a wrong CURL_CA_BUNDLE or --cacert path, or file permissions.
wget: cannot verify certificate
wget cannot verify a site's certificate when the chain is incomplete, the CA bundle is old or a proxy re-signs traffic. Fix the server or set the CA file.
OpenSSL error 20: unable to get local issuer certificate
OpenSSL error 20 means the issuer of a certificate in the chain is not in the trust store or supplied intermediates. Read the depth, then add what is missing.
OpenSSL error 10: certificate has expired
OpenSSL error 10 means a certificate in the chain was past its notAfter date when checked. Find the depth, then renew, replace the intermediate or fix a clock.
OpenSSL: unable to load certificate
OpenSSL says unable to load certificate when the file is not a PEM certificate: DER, PKCS#7, PKCS#12, a key or a damaged paste. How to detect and convert it.
Maven: PKIX path building failed
Maven fails with PKIX path building failed when the JVM running it does not trust the repository's certificate, usually behind a proxy or with a private Nexus.
Composer SSL operation failed
Composer's "SSL operation failed" or curl error 60 means PHP could not verify Packagist's certificate. Fix PHP's CA bundle; never set disable-tls.
unexpected eof while reading
OpenSSL 3 reports unexpected eof while reading when the peer closes TCP without a TLS close_notify. Check whether the handshake or shutdown failed.
dh key too small
dh key too small means the server offered Diffie-Hellman parameters below your OpenSSL security level. Use ECDHE or 2048-bit DH on the server.
Could not create SSL/TLS secure channel
Windows PowerShell and .NET Framework could not negotiate TLS with the server, usually because TLS 1.2 is not offered. Enable it the supported way.
AWS CLI SSL validation failed
botocore prints SSL validation failed for <endpoint> when the TLS check fails. Fix it with ca_bundle, the right endpoint, or path-style S3.
CondaSSLError
CondaSSLError means conda's HTTPS download failed a certificate check, or Python's ssl module could not load. Use truststore or a CA bundle.
Platforms and DevOps
Docker: x509 signed by unknown authority
docker pull or login fails with x509: certificate signed by unknown authority when the daemon does not trust the registry's CA. Use certs.d, not insecure mode.
kubectl: x509 signed by unknown authority
kubectl reports x509: certificate signed by unknown authority when the CA in kubeconfig does not match the API server's. Refresh kubeconfig; don't skip TLS.
cert-manager: Certificate not Ready
A cert-manager Certificate stuck at READY False is failing in its CertificateRequest, Order or Challenge. Follow the chain with kubectl describe and fix it.
Cloudflare Error 525
Error 525 means Cloudflare could not complete a TLS handshake with your origin. Check the origin certificate, SNI, port 443 and cipher suites.
Cloudflare Error 526
Error 526 means Cloudflare in Full (strict) mode rejected your origin certificate: expired, self-signed, wrong hostname or incomplete chain.
Cloudflare Flexible redirect loop
Cloudflare Flexible mode plus an origin that redirects HTTP to HTTPS creates an endless redirect loop. How to confirm it and the three ways to fix it.
Docker TLS handshake timeout
docker pull fails with net/http: TLS handshake timeout when the registry handshake takes over 10 seconds. Fix the daemon proxy, path, MTU or mirror.
Mail and other protocols
SMTP STARTTLS: certificate verify failed
Mail apps fail with certificate verify failed after STARTTLS when the SMTP server certificate has the wrong name, is self-signed, expired or lacks its chain.
Outlook: name on the security certificate is invalid
Outlook warns that the certificate name is invalid when Autodiscover, IMAP or SMTP reaches a server whose certificate lacks that hostname. How to fix it.
PostgreSQL: sslmode verify-full error
libpq with sslmode=verify-full fails when root.crt is missing, the CA does not match or the host name differs. Supply the right CA; keep verify-full.
MySQL: ERROR 2026 SSL connection error
MySQL ERROR 2026 means the TLS handshake failed: CA verification, the auto-generated self-signed CA, a host name mismatch or no common TLS version.
Issuance and renewal
Certbot: Timeout during connect
Let's Encrypt could not reach your server on port 80 for the HTTP-01 challenge. Check DNS (including AAAA), firewalls and geo-blocking, or switch to DNS-01.
Let's Encrypt rate limit (rateLimited)
Let's Encrypt "too many certificates" or rateLimited means you hit an issuance limit. Identify the limit, wait for the refill and prevent a repeat.
CAA record prevents issuance
The CA found a DNS CAA record that does not list it, so it cannot issue. Find the CAA set that applies and add your CA's identifier, such as letsencrypt.org.
Certbot: unauthorized, Invalid response 403
Let's Encrypt reached your server but got 403 Forbidden for the HTTP-01 challenge file. A dotfile deny rule, a WAF or file permissions usually blocks the path.
Certbot: DNS problem NXDOMAIN
Let's Encrypt's resolvers say the name does not exist. Check for typos, missing records, nameserver delegation, a suspended domain or a TXT in the wrong zone.
acme.sh: Verify error
acme.sh shows Verify error when the CA fails to validate your domain. The detail after it (404, 403, timeout, NXDOMAIN, wrong TXT) names the failed check.
Certbot: Could not bind TCP port 80
Certbot's standalone plugin cannot listen on port 80 because a web server already uses it, or Certbot is not root. Switch plugin or stop the server.
Certbot: SERVFAIL looking up CAA
Let's Encrypt's resolvers got SERVFAIL when checking your CAA records. Usually broken DNSSEC, a nameserver that mishandles CAA queries, or an outage.
FAQ
Is an SSL error always the website's fault?
No. Many cases come from the visitor's side: a wrong system clock, antivirus HTTPS scanning, a corporate proxy, public Wi-Fi login pages or an outdated device. Check the site with the SSL checker first; if it passes from our servers, the problem is local to the device or network that shows the error.
Is it safe to click "Proceed anyway" on a certificate warning?
Only on a device or site you control and when you know why the warning appears, such as your own router's admin page with a self-signed certificate. On any site where you log in, pay or type personal data, do not bypass the warning: the browser cannot prove you are talking to the real server, so anything you send could be read or changed.
Why does the same site work in one browser and fail in another?
Browsers use different trust stores and different repair tricks. Chrome and Safari often download a missing intermediate certificate on their own, Firefox relies on a preloaded list of public intermediates, and most command-line tools and apps do neither, so an incomplete chain fails only in some clients. Run curl ssl.sh/example.com or the chain builder to see the chain the server sends.