SSL Certificate Errors: Decode & Fix NET::ERR_CERT_* and More
← Blog

SSL Certificate Errors: Decode & Fix NET::ERR_CERT_* and More

11 min read

Almost every "Your connection is not private" screen resolves to one of seven distinct SSL/TLS failures, and the browser tells you which one in a code string most people scroll past: NET::ERR_CERT_DATE_INVALID, ERR_CERT_AUTHORITY_INVALID, ERR_CERT_COMMON_NAME_INVALID, ERR_SSL_VERSION_OR_CIPHER_MISMATCH, a mixed-content block, a raw handshake failure, or an HSTS interstitial you cannot click through. Each has a precise cause and a precise fix.

A browser SSL warning is not one problem - it is seven, and the error code names which. This is how to read the code, probe the real cause, and fix it at the server, one failure at a time.

The fastest way to skip the guesswork is to probe the certificate directly: run your domain through the SSL Certificate Checker to see the exact expiry, chain, SANs, and protocol before you touch a config file.

First, Read the Error Code - It Names the Failure Class

The error string is diagnostic, so read it before you change anything. Chrome and Edge surface a NET::ERR_CERT_* or ERR_SSL_* token on the warning screen; Firefox uses SEC_ERROR_* and SSL_ERROR_*; Safari phrases it in prose. Every token maps to one failure class below. The certificate itself is a static object - it either expired, chains to an untrusted root, lists the wrong names, negotiates an unsupported protocol, or is being undermined by how the page loads. Identify the class from the code, then jump to that section - the rest do not apply.

To read the certificate the way a browser does, probe it from the terminal:

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

That returns the validity window (notBefore/notAfter), the subject (which names the cert covers), and the issuer (who signed it) - the three fields that decide five of the seven errors.

Error 1: NET::ERR_CERT_DATE_INVALID - Expired or Not-Yet-Valid

This error means the current time sits outside the certificate's validity window - almost always because the certificate expired, occasionally because a new cert's notBefore date is in the future or the server clock is wrong. Let's Encrypt certificates last 90 days, and unattended renewal is the single most common thing that silently breaks.

How to Probe It

Read the notAfter date with the openssl one-liner above, or run the SSL Checker for the exact expiry and days-remaining. If notBefore is in the future, the certificate was issued early - check the server's system clock with date -u, because a drifted clock invalidates an otherwise-good cert.

How to Fix It

  1. Renew with your CA - certbot renew for Let's Encrypt, or reissue through your hosting panel.
  2. Reload the web server so it serves the new cert: sudo systemctl reload nginx (Nginx) or sudo apachectl graceful (Apache). Renewal without a reload leaves the old cert in memory.
  3. Confirm the fix by re-probing - the notAfter date should jump forward.

Enable auto-renewal with a certbot systemd timer or cron job, if you are on Let's Encrypt's 90-day cycle. The durable fix is not renewing once - it is being alerted 30 days before expiry so renewal never becomes an outage.

Error 2: ERR_CERT_AUTHORITY_INVALID - Broken Chain or Untrusted Root

This error means the browser cannot build a trust path from your certificate to a root it already trusts - the intermediate certificate is missing, the cert is self-signed, or it was issued by a CA the client does not recognize. The tell is environmental: the site loads on your desktop (which cached the intermediate from another site) but fails on a fresh mobile browser that never saw it.

How to Probe It

Run openssl s_client -servername yoursite.com -connect yoursite.com:443 and read the certificate chain it prints. A healthy chain shows your leaf certificate plus one or more intermediates ending at a known root. A chain with a single certificate and verify error:num=20:unable to get local issuer certificate is the missing-intermediate signature. The SSL Checker flags an incomplete chain explicitly.

How to Fix It

  • Serve the full chain, not the bare leaf. In Nginx, ssl_certificate must point to fullchain.pem (leaf + intermediates), never cert.pem alone.
  • In Apache, set SSLCertificateFile to the full chain (2.4.8+) or use SSLCertificateChainFile for the intermediates on older builds.
  • Reload the server and re-probe until the chain resolves to a trusted root.

If the certificate is self-signed (fine for a staging box, never for production), the fix is to issue a real cert from a trusted CA.

Error 3: ERR_CERT_COMMON_NAME_INVALID - Name Mismatch

This error means the hostname in the address bar is not listed on the certificate. A certificate is only valid for the exact names in its Subject Alternative Name (SAN) field. Hitting https://shop.example.com with a cert that covers only example.com and www.example.com triggers it - as does the reverse, and any typo'd subdomain.

How to Probe It

List the names the certificate actually covers:

echo | openssl s_client -servername yoursite.com -connect yoursite.com:443 2>/dev/null | openssl x509 -noout -ext subjectAltName

Compare that SAN list against the hostname you are visiting. The SSL Checker shows the covered SANs alongside the domain you probed, so a mismatch is immediate.

How to Fix It

  • Reissue the certificate with every hostname you serve in the SAN list.
  • Use a wildcard certificate (*.example.com) if you add subdomains often - one cert then covers every first-level subdomain.
  • Redirect apex-to-www (or the reverse) so visitors only ever reach a covered name, if the mismatch is a canonicalization slip rather than a missing SAN.

Error 4: ERR_SSL_VERSION_OR_CIPHER_MISMATCH - Protocol or Cipher

This error means the browser and server share no common TLS version or cipher suite, so the handshake ends before a certificate is ever exchanged. It surfaces after you disable old protocols too aggressively, or when a legacy server only offers protocols modern browsers have dropped. Chrome removed support for TLS 1.0 and 1.1 in 2020; a server stuck on them has nothing left to negotiate.

How to Probe It

Ask the server which protocols it will accept:

openssl s_client -connect yoursite.com:443 -tls1_2   # succeeds if 1.2 is offered
openssl s_client -connect yoursite.com:443 -tls1_3   # succeeds if 1.3 is offered

A healthy modern server accepts TLS 1.2 and 1.3 and rejects everything older. The SSL Checker reports the negotiated protocol and flags weak or legacy ciphers.

How to Fix It

  • Enable TLS 1.2 and 1.3 explicitly. In Nginx: ssl_protocols TLSv1.2 TLSv1.3;.
  • Set a modern cipher list and drop RC4, 3DES, and anything MD5-based.
  • Reload and re-probe both protocol versions to confirm at least one succeeds.

Error 5: Mixed Content - HTTPS Page Loading HTTP Resources

This is not a certificate fault - the padlock breaks because an HTTPS page requests a script, stylesheet, image, or font over plain http://. Browsers block active mixed content (scripts, iframes) outright and flag passive mixed content (images), stripping the padlock and, for scripts, breaking the page.

How to Probe It

Open DevTools and read the Console for Mixed Content: The page at 'https://…' was loaded over HTTPS, but requested an insecure resource 'http://…'. Each warning names the exact offending URL.

How to Fix It

  • Rewrite hard-coded http:// asset URLs to https:// (or protocol-relative //).
  • Add Content-Security-Policy: upgrade-insecure-requests to auto-upgrade requests at the browser, if you cannot edit every reference by hand.

The full walkthrough - including how to find every offending resource at scale - is in the fix mixed content guide.

Error 6: SSL Handshake Failed - Negotiation Collapsed

A raw "SSL handshake failed" means the TLS negotiation itself broke before completion - a superset symptom that usually traces to a missing SNI, a firewall interrupting the exchange, or the protocol/cipher mismatch from Error 4. The server never got far enough to present a valid certificate.

How to Probe It

Run a verbose handshake and read where it stops:

openssl s_client -connect yoursite.com:443 -servername yoursite.com -tlsextdebug

Watch for the -servername (SNI) being echoed back. On shared IPs, omitting SNI returns the wrong virtual host's certificate and fails - which is exactly why the probe includes it. If the connection resets mid-handshake, a network middlebox or firewall is interrupting TLS.

How to Fix It

  • Ensure the client and any CDN/proxy send SNI - required whenever one IP serves multiple certificates.
  • Confirm the server's server_name / ServerName matches the requested host so the right cert is selected.
  • Resolve any protocol mismatch (Error 4) first, since it presents as a generic handshake failure.

Error 7: HSTS Interstitial - The Warning You Cannot Bypass

When a domain sends an HSTS header, the browser refuses to let users click through any certificate warning at all - the "Proceed anyway" link disappears. This is HSTS working as designed: once a browser records the Strict-Transport-Security policy, any cert error on that domain becomes a hard, un-bypassable wall until you fix the underlying cert.

How to Probe It

Check whether the domain sends the header and how long the policy lasts:

curl -sI https://yoursite.com | grep -i strict-transport-security

A long max-age (a year is common) plus includeSubDomains means every subdomain inherits the un-bypassable behavior. Audit the full header set with the Security Headers Checker.

How to Fix It

The only fix is to repair the actual certificate error (Errors 1-4) - HSTS deliberately removes the escape hatch. Understand max-age, includeSubDomains, and the preload list before you widen the policy, because an HSTS mistake locks users out until the cert is genuinely valid: the full mechanics are in the HSTS preloading guide.

Diagnose Any SSL Error in Under 3 Seconds

You can run every openssl probe above by hand, or read all of it at once. The SSL Certificate Checker returns the expiry date, full chain status, covered SANs, negotiated protocol, and cipher strength from multiple regions in under 3 seconds - the same five fields that decide which of the seven errors you are looking at, without a single command.

Probe your SSL certificate now →

Automate the Check So It Never Regresses

Every SSL error above is a regression waiting to happen - a renewal that silently fails, a config change that drops an intermediate, a protocol tightened too far. Catch it before your users do by dispatching the UpMonitor CLI in your deploy pipeline:

npx @upmonitor/cli check https://yoursite.com --checks ssl

Wire that into CI before the deploy promotes and a broken chain or an expired cert fails the build instead of failing in a visitor's browser three days later. See the CLI documentation for CI/CD exit-code handling.

Frequently Asked Questions

Why does my site work in Chrome but show an SSL error on mobile?

The certificate chain is incomplete. Desktop Chrome often caches intermediate certificates from previous sites and fills the gap silently, while a fresh mobile browser cannot - so it reports ERR_CERT_AUTHORITY_INVALID. Serve the full chain (Error 2) to fix it everywhere.

Is NET::ERR_CERT_DATE_INVALID always an expired certificate?

Usually, but not always. It fires whenever the current time is outside the validity window - which also happens when a certificate's notBefore date is still in the future, or when the server's own clock is wrong. Probe the notBefore and notAfter dates before assuming expiry.

Can I just click "Proceed anyway" to bypass an SSL warning?

Only when the domain does not enforce HSTS. If it sends a Strict-Transport-Security header (Error 7), the browser removes the bypass entirely and the fix is to repair the certificate itself.

How long are SSL certificates valid?

Let's Encrypt certificates last 90 days; commercial CAs typically issue for up to 398 days (the current browser-enforced maximum). Shorter lifetimes make automated renewal and expiry monitoring non-optional.

Conclusion

An SSL warning is a labeled failure, not a mystery: the browser's error code names one of seven classes - expiry, broken chain, name mismatch, protocol mismatch, mixed content, handshake collapse, or an HSTS wall - and each has a specific openssl probe and a specific server-side fix. Read the code, probe the certificate, apply the matching fix, then automate the check so the same failure never ships twice. For a broader pass on headers, redirects, and crawlability around the same domain, work through the technical SEO audit guide.

Check your SSL certificate for all seven errors, free →