← Back to Blog
NET::ERR_CERT_AUTHORITY_INVALID means the browser cannot verify the authority that issued the certificate. This is different from an expired certificate (ERR_CERT_DATE_INVALID) — here, the certificate itself may be valid, but the browser can't trust the chain connecting it to a recognized root CA. Here's every cause and the fix for each.
🔒
Your connection is not private
NET::ERR_CERT_AUTHORITY_INVALID
The server's certificate authority is not trusted. The site may be using a self-signed certificate, or an intermediate CA is missing from the chain.
Cause 1: Missing Intermediate Certificate (Most Common)
The most frequent cause. Your certificate was issued by an intermediate CA, but your server only sends the end-entity certificate — not the intermediate. Browsers may accept this on desktop (they cache intermediates) but mobile Safari and curl will fail.
# Diagnose: count how many certs your server sends:
echo | openssl s_client -connect yourdomain.com:443 -showcerts 2>/dev/null \
| grep -c "BEGIN CERTIFICATE"
# Should show 2 or 3 (end-entity + 1-2 intermediates)
# If shows 1, you're missing the intermediate
# Fix: include intermediate in your certificate bundle
cat yourdomain.crt intermediate.crt > fullchain.crt
# Apache — use fullchain file:
SSLCertificateFile /etc/ssl/certs/fullchain.crt
# Nginx — always use fullchain file:
ssl_certificate /etc/ssl/certs/fullchain.crt;
# Reload web server:
sudo systemctl reload apache2 # or: nginx -s reload
Cause 2: Self-Signed Certificate
Self-signed certificates are not trusted by browsers because there's no CA to vouch for them. Only appropriate for local development or internal tools where you explicitly add an exception.
# Identify if cert is self-signed (issuer = subject):
echo | openssl s_client -connect yourdomain.com:443 2>/dev/null \
| openssl x509 -noout -issuer -subject
# If issuer and subject are identical → self-signed
# Fix for production: get a real certificate from Let's Encrypt:
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
# Fix for local development: use mkcert to create a locally-trusted cert:
mkcert -install # installs local CA in system/browser trust stores
mkcert localhost 127.0.0.1 ::1 # creates locally trusted cert
Cause 3: Certificate Chain in Wrong Order
The chain must be in order from end-entity to root: your cert first, then intermediate(s), then optionally root. If the order is reversed, the chain validation fails.
# Check the order of certificates in your bundle:
openssl crl2pkcs7 -nocrl -certfile fullchain.crt \
| openssl pkcs7 -print_certs -noout
# Correct order:
# subject=CN=yourdomain.com ← end-entity first
# subject=CN=Intermediate CA
# subject=CN=Root CA ← root last (often omitted)
# Wrong order (reversed):
# subject=CN=Intermediate CA ← intermediate first (WRONG)
# subject=CN=yourdomain.com
# Fix: rebuild the bundle in correct order:
cat yourdomain.crt intermediate.crt > fullchain.crt
Cause 4: Untrusted or Private CA
Some enterprise setups use internal certificate authorities not recognized by public browsers. Common in corporate intranets.
# Check which CA issued the certificate:
echo | openssl s_client -connect yourdomain.com:443 2>/dev/null \
| openssl x509 -noout -issuer
# issuer=CN=Internal-CA ← not publicly trusted
# For internal/intranet use — distribute the CA certificate to clients:
# Windows: certmgr.msc → Trusted Root Certification Authorities → Import
# macOS: Keychain Access → System → Certificates → Import (set trust to Always Trust)
# Linux: sudo cp internal-ca.crt /usr/local/share/ca-certificates/ && sudo update-ca-certificates
# For public-facing sites: switch to a publicly trusted CA:
sudo certbot --apache -d yourdomain.com
Cause 5: OpenSSL CA Bundle Out of Date
Server-to-server requests (curl, Python requests, Node.js) use the system CA bundle, not the browser trust store. An outdated CA bundle may not include newer root CAs.
# Check CA bundle version:
openssl version -d
# OPENSSLDIR: "/usr/lib/ssl" ← this is where the bundle is
# Update CA certificates on Ubuntu/Debian:
sudo apt update && sudo apt install --reinstall ca-certificates
sudo update-ca-certificates
# Test with verbose SSL to see chain verification:
curl -vvv https://yourdomain.com 2>&1 | grep -E "verify|issuer|subject|OK|error"
Diagnosis: Full Chain Check with OpenSSL
$ openssl s_client -connect yourdomain.com:443 2>/dev/null | head -20
# BROKEN chain (only 1 cert, missing intermediate):
depth=0 CN=yourdomain.com
verify error:num=20:unable to get local issuer certificate
Verify return code: 20 (unable to get local issuer certificate)
# CORRECT chain (depth shows full path):
depth=2 C=US, O=DigiCert Inc, CN=DigiCert Global Root G2
verify return:1
depth=1 C=US, O=DigiCert Inc, CN=DigiCert TLS RSA SHA256 2020 CA1
verify return:1
depth=0 CN=yourdomain.com
verify return:1
Verify return code: 0 (ok)
Quick Diagnosis Checklist
- Run:
openssl s_client -connect yourdomain.com:443 2>&1 | grep "Verify return code" — "0 (ok)" = chain valid
- Count certs:
openssl s_client -showcerts -connect yourdomain.com:443 2>/dev/null | grep -c "BEGIN CERT" — should be ≥2
- Check self-signed: issuer = subject in
openssl x509 -noout -issuer -subject
- Test from a different network (eliminates local antivirus/firewall MITM interception)
- Check if antivirus "HTTPS scanning" is active — it replaces certificates with its own CA, which may not be trusted
Check your certificate chain with our SSL checker
Verify your complete certificate chain, intermediate certificates, and trust status in seconds.