Introduction
When a browser displays Your connection is not secure or NET::ERR_CERT_AUTHORITY_INVALID, it means the certificate validation process has failed. The root cause can range from self-signed certificates to missing intermediates, expired root CAs, or domain mismatches. Identifying exactly why the browser doesn't trust your certificate is the first step toward fixing it.
Symptoms
- Browser shows
NET::ERR_CERT_AUTHORITY_INVALIDor similar warning - Different browsers show different trust errors for the same site
- Mobile devices fail while desktop browsers work (or vice versa)
curlreturnsSSL certificate problem: unable to get local issuer certificate- Internal applications fail with certificate verification errors
- SSL Labs shows validation failures in the certification paths section
Common Causes
- Self-signed certificate not added to trust store
- Intermediate certificate missing from chain
- Certificate issued by non-standard or internal CA
- CA root not in client's trust store (old or obscure CA)
- Certificate hostname doesn't match request URL
- Certificate has been revoked by the CA
- System clock on client is wrong
Step-by-Step Fix
Step 1: Determine the Trust Failure Type
Run a comprehensive certificate check:
```bash # Check certificate details openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text
# Check verification result echo | openssl s_client -connect example.com:443 -servername example.com 2>&1 | grep -E "Verify return|depth|error"
# Get full chain info echo | openssl s_client -connect example.com:443 -showcerts 2>/dev/null | grep -E "s:|i:|Verify" ```
Review the output for these key indicators:
# Look for these error messages
echo | openssl s_client -connect example.com:443 2>&1 | grep -i "error\|alert"Step 2: Check if Self-Signed or Private CA
```bash # Check issuer - if issuer equals subject, it's self-signed openssl x509 -in /path/to/cert.pem -noout -subject -issuer
# Self-signed will show something like: # subject=CN = example.com # issuer=CN = example.com
# Private CA will show your organization's CA name ```
If self-signed or private CA, you need to add it to client trust stores:
```bash # For Linux clients sudo cp ca-cert.pem /usr/local/share/ca-certificates/ sudo update-ca-certificates
# For Windows (via PowerShell as Admin) Import-Certificate -FilePath "ca-cert.pem" -CertStoreLocation Cert:\LocalMachine\Root
# For macOS sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca-cert.pem ```
Step 3: Fix Missing Intermediate Chain
```bash # Check how many certs are being served echo | openssl s_client -connect example.com:443 -showcerts 2>/dev/null | grep -c "BEGIN CERTIFICATE"
# Should be 2+ (server cert + intermediate(s)) # If only 1, you're missing intermediates
# Get intermediate from your CA or use certbot's fullchain # For Let's Encrypt, use the fullchain.pem file ls -la /etc/letsencrypt/live/example.com/ # Should see: cert.pem, chain.pem, fullchain.pem, privkey.pem ```
Configure your web server to use the full chain:
# Nginx - use fullchain.pem, not cert.pem
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;Step 4: Verify Domain Matching
```bash # Check Subject Alternative Names openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -text | grep -A 1 "Subject Alternative Name"
# Check Common Name openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -subject ```
The domain accessed must match one of the SANs or the CN exactly.
Step 5: Check for Revocation
```bash # Get OCSP URL from certificate openssl x509 -in cert.pem -noout -ocsp_uri
# Check revocation via OCSP openssl ocsp -issuer chain.pem -cert cert.pem -url $(openssl x509 -in cert.pem -noout -ocsp_uri) -resp_text
# Or check CRL openssl x509 -in cert.pem -noout -text | grep -A 4 "CRL Distribution Points" ```
Step 6: Comprehensive Verification
```bash # Full verification against system trust store openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt fullchain.pem
# With specific host verification openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt -verify_hostname example.com fullchain.pem
# Test actual connection curl -vI https://example.com 2>&1 | grep -E "SSL certificate verify|subject:|issuer:" ```
Common Pitfalls
- Using
cert.peminstead offullchain.pemin Nginx config - Forgetting to add intermediate to Java keystore separately
- Testing on the same machine where you added the cert to trust store
- Not realizing different browsers/devices have different root stores
- Certificate works in Chrome but fails in Firefox (different trust stores)
Best Practices
- Always use certificates from trusted public CAs for public-facing services
- Use fullchain certificate bundles in server configuration
- Test with SSL Labs after certificate changes
- Set up certificate monitoring that checks trust chain validity
- Document certificate renewal process and CA relationships
- For internal services, establish a private CA and distribute root cert properly
Related Issues
- SSL Certificate Chain Incomplete
- SSL Certificate Name Mismatch
- SSL Certificate Expired
- SSL Self-Signed Certificate