All articles
·9 min read

'Your connection is not private': why a certificate isn't trusted and how to fix it

When a browser says your SSL certificate is "not trusted" — Chrome's full-page "Your connection is not private" interstitial — it means the certificate it received could not be traced back to a certificate authority the device already trusts. The certificate might be technically valid and unexpired and still fail, because trust is about the chain, not the certificate alone. This guide explains what "not trusted" actually means, how to tell whether it's the server's fault (everyone sees it) or just your machine (only you see it), and the exact fix for each of the seven common causes.

What "not trusted" actually means

Every HTTPS connection presents a certificate signed by an intermediate certificate authority, which is itself signed by a root authority. The client walks that path — leaf, then intermediate(s), then root — checking the signature at each hop. The connection is trusted only if the final link terminates at a root certificate already present in the client's trust store: the curated list of root CAs shipped with the operating system or browser.

"Not trusted" means that walk failed. Either the chain didn't reach a root at all, the root it reached isn't in the trust store, or one of the links was invalid (wrong name, expired, self-signed). The certificate's own contents can be fine; what's broken is the path to a trust anchor. That distinction is the most useful thing to internalize, because the problem is almost never "the certificate is bad" and almost always "the chain or the trust store is wrong." If leaf, intermediate, and root are fuzzy concepts, read the certificate chain explained first — everything below assumes that mental model.

Decode the browser error code first

Browsers hide the precise reason behind a friendly headline, but the machine-readable code underneath tells you exactly which check failed. In Chrome, click anywhere on the warning page and type thisisunsafe, or expand "Advanced" to see the code. Here's what each one implies.

  • ERR_CERT_AUTHORITY_INVALID (Chrome / Edge) — the chain didn't terminate at a trusted root. This is the catch-all for "not trusted": missing intermediate, self-signed cert, or a private CA the client doesn't know. See what ERR_CERT_AUTHORITY_INVALID means.
  • SEC_ERROR_UNKNOWN_ISSUER (Firefox) — Firefox's wording for the same failure. Firefox ships its own trust store, independent of the OS, which is why a site can work in Chrome but fail in Firefox (or vice versa). The Node.js sibling of this error is unable to verify the first certificate, which almost always means a missing intermediate.
  • ERR_CERT_DATE_INVALID (Chrome) / SEC_ERROR_EXPIRED_CERTIFICATE (Firefox) — a certificate in the path is outside its validity window. Usually the leaf expired; occasionally the client's clock is wrong. See net::ERR_CERT_DATE_INVALID explained and what an expired certificate means and how to fix it.
  • ERR_CERT_COMMON_NAME_INVALID (Chrome) / SSL_ERROR_BAD_CERT_DOMAIN (Firefox) — the certificate is trusted, but it doesn't cover the hostname you asked for. The trust chain is fine; the name is wrong. See ERR_TLS_CERT_ALTNAME_INVALID explained.

The last two are technically different failures from "untrusted issuer" — they're name and time problems — but browsers lump them under the same interstitial, so people search for all of them as "certificate not trusted." Identifying the exact code narrows seven possible causes down to one or two.

Is it the server's problem or your machine's problem?

This is the question that decides everything. A server-side problem means the certificate or chain the server sends is wrong, so every visitor sees the warning — it's an outage and you fix it on the server. A client-side problem means the server is fine but your specific device can't validate it, so only you (and others with the same misconfiguration) are affected.

A 30-second test settles it: check the same URL from a network and device you don't control — a phone on cellular data, a colleague in another office, or an external checker like the SSL check tool, which validates from a neutral vantage point outside your network.

  • Everyone, everywhere sees the warning → server-side. Causes 1–4 below.
  • Only you see it; it works elsewhere → client-side. Causes 5–7 below.

Resist the urge to fix the server before running this test. A surprising number of "the cert is broken" tickets are one developer's antivirus or a stale system clock, and redeploying certificates won't touch that.

Server-side causes (everyone sees it)

1. Missing intermediate certificate

The most common cause by far. The server is configured to send only the leaf certificate and omits the intermediate(s) that link it to the root. Browsers often mask this — caching intermediates from previous sites or fetching them via the AIA extension — which is why a site can look fine in Chrome on your laptop but fail in strict clients: Node.js, Java, mobile apps, curl, and older Android. The result is the maddening "works for me, broken for them" bug.

Confirm what the server actually sends:

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

Read the Certificate chain block at the top. A healthy chain lists the leaf (s: your domain) and the intermediate (s: the CA), each with its issuer (i:) above it. If the block stops after the leaf, the intermediate is missing.

Certificate chain
 0 s:CN=example.com
   i:C=US, O=Let's Encrypt, CN=E5
 1 s:C=US, O=Let's Encrypt, CN=E5
   i:C=US, O=Internet Security Research Group, CN=ISRG Root X1

The fix is server-side: serve the full chain, not just the leaf. With Let's Encrypt that means pointing your server at fullchain.pem instead of cert.pem. The exact directive depends on your stack — see the Nginx guide or Apache guide for the right config line and reload command. The deeper walkthrough is in the certificate chain explained.

2. Self-signed certificate

A self-signed certificate is signed by its own private key rather than by a CA, so the chain terminates at a root that exists nowhere in any public trust store. No client will trust it by default — that's working as designed. Chrome shows ERR_CERT_AUTHORITY_INVALID; Node.js reports DEPTH_ZERO_SELF_SIGNED_CERT or self signed certificate in certificate chain.

Spot one by checking whether the subject equals the issuer:

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

If subject= and issuer= are identical, it's self-signed. The fix depends on intent. For a public-facing site, replace it with a CA-issued certificate (Let's Encrypt is free and automated). For internal services where self-signing is deliberate, you distribute your own root to the clients instead — covered in self-signed certificates explained and monitoring internal and private certificates.

3. Expired certificate

A certificate past its notAfter date breaks the chain at that link, even if everything else is correct. Chrome shows ERR_CERT_DATE_INVALID; this is a genuine outage that hits every visitor at once, usually because a renewal hook silently failed and nobody noticed. Check the dates:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -dates
notBefore=Mar  1 00:00:00 2026 GMT
notAfter=May 30 23:59:59 2026 GMT

If notAfter is in the past, renew and — critically — reload the server, because a renewed file on disk does nothing until the running process loads it. The full diagnosis and fix path is in what an expired certificate means and how to fix it. Note: an intermediate can expire too, which breaks trust for everyone even though your leaf looks fine — the -showcerts output from cause 1 will reveal it.

4. Hostname or SAN mismatch

Here the chain is trusted but the certificate doesn't list the hostname you connected to. Modern clients validate against the Subject Alternative Name (SAN) extension and ignore the legacy Common Name entirely, so a cert issued for example.com will be rejected on www.example.com unless www is in its SAN list. Chrome shows ERR_CERT_COMMON_NAME_INVALID; Node.js shows ERR_TLS_CERT_ALTNAME_INVALID. Dump the SANs:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -ext subjectAltName
X509v3 Subject Alternative Name:
    DNS:example.com, DNS:www.example.com

If the host you're hitting isn't in that list, reissue the certificate with the correct names, or use a wildcard certificate if you're juggling many subdomains. This often surfaces right after adding a new subdomain or pointing a vanity domain at an existing host.

Client-side causes (only you see it)

If the external check from earlier came back clean, the server is fine and the problem lives on the affected machine. These never produce an outage for real users — but they will eat hours if you debug them as if they were server problems.

5. The client clock is wrong

Validity is checked against the device's current time. If the clock is badly wrong — a freshly imaged VM, a Raspberry Pi with no RTC, a laptop with a dead CMOS battery — a perfectly valid certificate looks expired or not-yet-valid, and you get ERR_CERT_DATE_INVALID everywhere on that machine. Check and fix the time:

date
sudo timedatectl set-ntp true   # Linux: enable NTP sync

On a server with no network time, certificates and TLS in general behave erratically until the clock is corrected. If the warning vanishes the moment you fix the date, this was your cause.

6. A private or enterprise root CA isn't installed

Inside a company, internal sites are frequently signed by a private corporate CA. That CA is legitimate, but its root must be installed in each client's trust store for connections to validate. New laptops, containers, and CI runners that skipped the provisioning step will reject internal sites with ERR_CERT_AUTHORITY_INVALID while everyone with the root installed is unaffected.

The fix is to install the organization's root certificate into the OS (and, for Firefox, its separate store). On Debian/Ubuntu:

sudo cp internal-root-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

This is a provisioning gap, not a certificate defect — verify the cert externally only if it's internet-facing. For watching certs behind a private CA, see monitoring internal and private certificates.

7. Antivirus or a corporate proxy is intercepting TLS

Many endpoint security products and corporate web proxies perform TLS interception: they terminate the connection, inspect it, and re-sign it on the fly with their own root CA. When that root is installed on managed devices, this is invisible. When it isn't — a personal device on the corporate Wi-Fi, a misconfigured agent — the browser sees an unexpected issuer and shows ERR_CERT_AUTHORITY_INVALID.

The tell is the issuer. If openssl or the browser's certificate viewer shows the certificate was issued by your antivirus vendor (e.g. "Kaspersky", "ESET", "Bitdefender") or "Some-Corp Proxy CA" instead of the real public CA, interception is in play:

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

Fix it by installing the proxy's root on the device, disabling HTTPS scanning in the antivirus, or getting off the intercepting network. The certificate the real server sent is fine; you're just seeing a substitute.

How to diagnose, step by step

A repeatable order beats guessing:

  1. Read the exact error code (ERR_CERT_*, SEC_ERROR_*). It points at one or two causes immediately.

  2. Open the browser certificate viewer. Click the warning, view the certificate, and read the issuer and validity dates. An unexpected issuer means interception or a private CA; a past date means expiry or a clock problem.

  3. Inspect the served chain with the tool that shows ground truth:

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

    A chain that stops at the leaf means a missing intermediate; identical subject and issuer means self-signed. The full openssl playbook is in checking a certificate with openssl.

  4. Check from outside your network with the SSL check tool to confirm whether it's server-side (everyone) or client-side (just you). This one step prevents most wasted effort.

Work top to bottom and you'll classify any "not trusted" warning correctly in a couple of minutes.

Monitor it automatically

Most "your connection is not private" outages are missing-intermediate or expired-certificate problems that nobody saw coming — exactly the kind a daily check catches before your users do. SSLNudge validates the full chain to a trusted root, the served intermediates, the SANs, and the expiry date every day, and alerts you the moment trust breaks. Add your domains once and let it watch them. Sign in to start monitoring.

Stop tracking expiry dates by hand

SSLNudge checks your certificates daily and alerts you before they expire.

Start free