Troubleshooting

Your connection is not private: what each NET::ERR_CERT code means and how to fix it

Your connection is not private means the site's certificate failed a check: expired, untrusted (self-signed) or for another name. Visitor checks and fixes.

Published ~9 min read

Short answer: “Your connection is not private” means the site’s certificate failed one of Chrome’s checks: it is expired (or your clock is wrong), it was not issued by a CA Chrome trusts, or it was issued for a different name. The NET::ERR_CERT_… code under the message says which. As a visitor, do not click through on a site where you log in or pay. As the site owner, the fix is almost always a Let’s Encrypt certificate that covers every name you serve, sent with its chain and renewed automatically.

The handshake itself worked; the browser stopped because it could not confirm who is on the other end. A certificate that does not check out looks exactly like an attacker sitting between you and the site, which is why Chrome opens with “Attackers might be trying to steal your information”. Edge says “Your connection isn’t private”, Safari “This Connection Is Not Private” and Firefox “Warning: Potential Security Risk Ahead”.

Which code do you see?

Chrome prints the code below the first paragraph. These are the certificate codes from Chromium’s net_error_list.h you will actually meet, with the sentence Chrome shows under Advanced, paraphrased:

CodeWhat Chrome saysUsual cause
NET::ERR_CERT_AUTHORITY_INVALIDThe certificate is not trusted by Chrome or your systemSelf-signed or private CA, a Cloudflare Origin CA certificate seen directly, antivirus or company interception
NET::ERR_CERT_COMMON_NAME_INVALIDIts certificate is from another namewww vs apex, an IP address, a wildcard one level too deep, a Wi-Fi login page
NET::ERR_CERT_DATE_INVALIDIt expired N days ago, or is not valid yetRenewal failed or nginx not reloaded, or your clock
NET::ERR_CERT_REVOKEDRevoked by its issuerThe owner or CA revoked it; there is no way past this page
NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIREDNot publicly disclosed in Certificate Transparency logsA publicly trusted certificate that was never logged; rare, contact the CA
NET::ERR_CERT_WEAK_SIGNATURE_ALGORITHMSigned with a weak algorithm such as SHA-1An old appliance or a hand-made certificate
NET::ERR_CERT_VALIDITY_TOO_LONGValid for too long to be trustworthyA public certificate with a lifetime beyond the CA/Browser Forum limit

Three related pages share the cause but carry their own headline:

  • Your clock is behind or Your clock is ahead: a NET::ERR_CERT_DATE_INVALID that Chrome traced to your device’s clock.
  • Connect to Wi-Fi: a hotel or airport network intercepted the connection to show its login page.
  • An application is stopping Chrome from safely connecting to this site: antivirus, firewall or filtering software inspects HTTPS but its root certificate is missing. Chrome names the program.

Firefox uses its own codes for the same failures: SEC_ERROR_UNKNOWN_ISSUER, MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT, SSL_ERROR_BAD_CERT_DOMAIN and SEC_ERROR_EXPIRED_CERTIFICATE. Two things that look similar are different errors. A page that loads but shows “Not secure” in the address bar is plain HTTP or mixed content, not a certificate failure. And This site can’t provide a secure connection means the handshake itself broke, covered in our ERR_SSL_PROTOCOL_ERROR guide.

Should you click “Proceed (unsafe)”?

Not on any site where you log in, pay or type anything private, and never on public Wi-Fi. The warning cannot tell a misconfigured server from an attacker, and neither can you from the page. A banking site with an invalid certificate is a reason to close the tab and try later from another network.

On many large sites the link is not there at all. A site that uses HSTS has told your browser to accept only a valid certificate, so Chrome shows You cannot visit example.com right now because the website uses HSTS and no way past. A revoked certificate is handled the same way. That is deliberate, not a bug.

Visitor fixes: when only you see it

If the site works on your phone over mobile data, the site is fine. Check these on the device that fails:

  • Date and time. Certificates are checked against your clock. A clock off by days, often after a dead CMOS battery or a dual-boot, breaks HTTPS everywhere. Turn on automatic time in the OS settings; on Linux, timedatectl status should show System clock synchronized: yes, and sudo timedatectl set-ntp true turns sync on.
  • A Wi-Fi login page. Hotels, trains and airports intercept your first request to show their terms. Open a plain-HTTP site such as http://neverssl.com to get the login page, accept it, then retry.
  • Antivirus HTTPS scanning, company proxies, parental filters, some VPNs. They replace every site’s certificate with one of their own. On a site that works, click the icon left of the address, then Connection is secure and Certificate is valid. If the issuer is your antivirus, employer or school rather than a public CA, every HTTPS connection is being inspected; turn off the scanning option for a test, or ask your IT department.
  • An old device. Systems that no longer get updates miss newer root certificates and fail on sites whose CA they do not know. Update the OS and the browser; on an unsupported system, Firefox brings its own root store.

Clearing the cache and Clear SSL state in Windows rarely change anything: the certificate is checked fresh on every connection. If many sites fail at once, it is the clock or an interception product; if one site fails everywhere, it is the site.

Site owner: read the certificate your server sends

Look at what the server actually sends, from outside, not at the files on disk. The first command runs the same checks a browser does, including the hostname; the second prints names, issuer and dates:

Linux · OpenSSL 1.1.1 or later
openssl s_client -connect example.com:443 -servername example.com \
  -verify_hostname example.com -brief </dev/null 2>&1 | grep -E 'Verification|verify error'

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

Replace every example.com with the failing name, www included if that is the one that fails. On macOS install OpenSSL 3 with Homebrew first; the built-in LibreSSL lacks -brief and -verify_hostname. A healthy site prints Verification: OK. These are real results from the badssl.com test hosts with OpenSSL 3.0, and the code Chrome shows for the same host:

Test hostopenssl s_client sayscurl saysChrome shows
expired.badssl.comverify error:num=10:certificate has expiredcertificate has expiredNET::ERR_CERT_DATE_INVALID
self-signed.badssl.comverify error:num=18:self-signed certificateself-signed certificateNET::ERR_CERT_AUTHORITY_INVALID
untrusted-root.badssl.comverify error:num=19:self-signed certificate in certificate chainself-signed certificate in certificate chainNET::ERR_CERT_AUTHORITY_INVALID
wrong.host.badssl.comverify error:num=62:hostname mismatchno alternative certificate subject name matches target host nameNET::ERR_CERT_COMMON_NAME_INVALID
incomplete-chain.badssl.comverify error:num=21:unable to verify the first certificateunable to get local issuer certificateUsually loads; see missing intermediate

Our SSL checker runs the same handshake from Amsterdam and reports expiry, hostname match, whether the chain is trusted and the certificates exactly as your server sent them. Behind Cloudflare it sees Cloudflare’s certificate, not yours.

Cause 1: a self-signed or placeholder certificate

A fresh server often answers HTTPS with a certificate nobody trusts: the snakeoil certificate from Debian’s ssl-cert package that Apache’s default-ssl site uses, one generated by a control panel, or a hand-made openssl req -x509. Browsers answer with NET::ERR_CERT_AUTHORITY_INVALID. Get a real one; with nginx and certbot that is one command once the DNS records point at the server and port 80 is open:

Debian · Ubuntu · nginx
sudo apt update && sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

Certbot fetches the certificate, writes the ssl_certificate lines into the matching server block and reloads nginx. List every name visitors use, each with its own -d.

Cause 2: the certificate is for a different name

NET::ERR_CERT_COMMON_NAME_INVALID means the name in the address bar is not in the certificate’s Subject Alternative Name list. Browsers ignore the old Common Name field. The SAN line from the command above is the whole truth. For wrong.host.badssl.com it reads:

Example output · wrong.host.badssl.com
subject=CN = *.badssl.com
issuer=C = US, O = Let's Encrypt, CN = YR1
notBefore=Sep 29 20:02:56 2026 GMT
notAfter=Dec 28 20:02:55 2026 GMT
X509v3 Subject Alternative Name:
    DNS:*.badssl.com, DNS:badssl.com

A wildcard covers exactly one label: *.badssl.com matches wrong.badssl.com but not wrong.host.badssl.com. The usual cases on a self-run server:

  • www or apex missing. example.com and www.example.com are two names. Add the missing one: sudo certbot --nginx -d example.com -d www.example.com --expand.
  • Opening the server by IP. https://203.0.113.10/ fails against a certificate for a domain. Use the name.
  • The wrong server block answers. A name with no matching server_name in a 443 block gets nginx’s default server and its certificate. Check that both the IPv4 and the IPv6 listen lines exist in the right block.
  • The DNS points elsewhere. An old A or AAAA record sends some visitors to a previous host that serves its own certificate. Compare dig +short example.com A and AAAA with your server’s addresses.

Cause 3: the certificate expired

Let’s Encrypt certificates last 90 days and certbot renews them with about 30 left, so an expired one means renewal has been failing for weeks, or it succeeded and nginx never loaded the new file. Check both:

Debian · Ubuntu
# What certbot has on disk, with expiry dates
sudo certbot certificates

# Would a renewal work now? (uses the staging CA, changes nothing)
sudo certbot renew --dry-run

# Is the renewal timer scheduled? (certbot.timer, or snap.certbot.renew.timer)
systemctl list-timers | grep -i certbot

If the file on disk is newer than the date the server sends, reload nginx: sudo nginx -t && sudo systemctl reload nginx. Certificates installed with --nginx reload on their own; with --webroot or --standalone, add a deploy hook. A failing dry run usually names a closed port 80 or a DNS record that no longer points here.

The deploy hook and Let’s Encrypt’s shorter lifetimes from 2027 on are in our SSL handshake failed guide.

Cause 4: a Cloudflare Origin CA certificate, seen directly

An Origin CA certificate from Cloudflare is trusted by Cloudflare only. Behind the orange cloud that is fine, because visitors see Cloudflare’s edge certificate. Per Cloudflare’s Origin CA documentation, visitors get untrusted-certificate errors as soon as you pause Cloudflare or set a record to DNS only. That gives NET::ERR_CERT_AUTHORITY_INVALID, and the certificate’s issuer names CloudFlare Origin SSL Certificate Authority (or its ECC twin). Turn the proxy back on, or replace the certificate with a Let’s Encrypt one, which works both ways.

Cause 5: the intermediate certificate is missing

If nginx points at cert.pem instead of fullchain.pem, the server sends its own certificate without the intermediate that links it to a trusted root. Desktop Chrome usually fetches the missing piece itself and Firefox ships known intermediates, so the site often looks fine in your browser while curl, Python, Java, payment webhooks and some phones fail with unable to get local issuer certificate. The fix is one word:

nginx · server block
ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;   # not cert.pem
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

Your own admin panels

Proxmox, Webmin, Cockpit, routers and NAS boxes ship with self-signed certificates, so NET::ERR_CERT_AUTHORITY_INVALID is expected on their ports. Rather than training yourself to click through, reach them through an SSH tunnel, or put them behind nginx on a real name with a Let’s Encrypt certificate, restricted to your IP.

For https://localhost in development, use mkcert: mkcert -install adds a local CA to your system and browser trust stores, and mkcert localhost 127.0.0.1 ::1 issues a certificate your dev server can load. The allow-insecure-localhost flag that older guides suggest has expired in current Chrome.

After the fix

  • The s_client command above prints Verification: OK for every name, apex and www, from a machine outside the server.
  • certbot renew --dry-run succeeds, and the timer is listed.
  • Ports 80 and 443 stay open: renewal with the HTTP challenge needs port 80. Our port 443 and 80 guide shows how to test both from outside.

On a VMHeaven Linux VPS you run certbot and nginx as root, and if a change locks you out, the browser VNC console in the panel still reaches the server.

Frequently asked

Is it safe to proceed when Chrome says your connection is not private?

Not on a site where you log in, pay or enter personal data, and never on public Wi-Fi: the warning cannot tell a misconfigured server from an attacker. The only routine exception is your own router, NAS or server panel with a self-signed certificate on your own network.

What does NET::ERR_CERT_AUTHORITY_INVALID mean?

The certificate was not issued by a certificate authority your browser or system trusts. Usual causes are a self-signed or placeholder certificate, a Cloudflare Origin CA certificate reached without the Cloudflare proxy, or antivirus or company software that intercepts HTTPS without its root certificate installed.

How do I fix NET::ERR_CERT_COMMON_NAME_INVALID on my site?

The certificate does not list the name in the address bar. Check its Subject Alternative Names with openssl, then reissue it with every name visitors use, for example 'sudo certbot --nginx -d example.com -d www.example.com --expand'. Remember that a wildcard covers only one label and that an IP address never matches a domain certificate.

Related articles

All troubleshooting