ERR_CERT_AUTHORITY_INVALID: Causes and the Fix

· 3 min read · 5 views · Getwebup

The short answer

ERR_CERT_AUTHORITY_INVALID means the browser does not trust whoever signed the certificate. The most common cause on a real site is a missing intermediate certificate: the server sends only the leaf, so the browser cannot build a chain to a trusted root. Run openssl s_client -connect yourdomain.com:443 and count the certificates in the chain - one certificate where there should be two is the whole problem.

Not sure which error you have? Decode Chrome's code first — "This Site Can't Be Reached" in Chrome: Every Code Decoded

ERR_CERT_AUTHORITY_INVALID is not a complaint about your certificate's contents. The dates are fine, the hostname matches - the browser simply cannot trace the signature back to an authority it trusts. On a live site with a real certificate, that almost always means one file is missing from the chain.

How trust is actually established

Your certificate is signed by an intermediate certificate, which is signed by a root certificate that ships with the browser. The browser only has the root. Your server is responsible for sending the intermediate alongside your own certificate so the chain can be completed.

If the server sends only its own certificate, the browser is left holding a signature from an issuer it has never heard of - and that is exactly what this error says.

The command that settles it

openssl s_client -connect yourdomain.com:443 -servername yourdomain.com </dev/null 2>&1 | head -30

Read the "Certificate chain" block at the top. A correctly configured server shows at least two entries: your certificate at depth 0, and the intermediate at depth 1. One entry, or a line reading verify error:num=20:unable to get local issuer certificate, is a missing intermediate and nothing else.

Fixing a missing intermediate

Your certificate authority gives you two files: the certificate itself, and a bundle (variously named ca-bundle.crt, chain.pem or intermediate.crt). Both need to reach the server.

nginx takes one concatenated file, leaf first:

cat yourdomain.crt intermediate.crt > /etc/ssl/certs/yourdomain-fullchain.crt
ssl_certificate     /etc/ssl/certs/yourdomain-fullchain.crt;
ssl_certificate_key /etc/ssl/private/yourdomain.key;

The order matters - leaf first, then intermediates. Reversed, nginx will start and browsers will still complain.

Apache takes them separately:

SSLCertificateFile      /etc/ssl/certs/yourdomain.crt
SSLCertificateChainFile /etc/ssl/certs/intermediate.crt
SSLCertificateKeyFile   /etc/ssl/private/yourdomain.key

With certbot, this is already handled - point the config at fullchain.pem and not cert.pem. Using cert.pem by mistake is one of the most common ways to produce this error on a Let's Encrypt certificate that is otherwise perfectly valid.

Reload rather than restart, then re-run the openssl command to confirm the chain now has two entries.

The other three causes

A self-signed certificate

Nothing signed it but itself, so no browser will ever trust it. For anything public-facing, replace it with a free Let's Encrypt certificate - certbot --nginx -d yourdomain.com takes about a minute. For an internal service, the correct fix is installing your own CA certificate into the trust store of the machines that need it, not clicking through the warning each time.

Security software intercepting HTTPS

Antivirus products with web scanning terminate TLS themselves and re-sign every site with their own root. When that root is not properly installed, every HTTPS site fails this way. The diagnosis is easy: if the error appears on every site rather than one, the certificate is not the problem and the security product is.

A device clock or an outdated root store

Old Android devices and long-unpatched machines can be missing roots that have been added since, and a badly wrong system clock can make a valid root appear untrusted. If the error is confined to one old device, check its date and its updates before touching the server.

Confirm the fix from outside your own machine

Your browser may have cached the chain it fetched earlier, so a local retest can mislead. Check from somewhere with no cache at all:

curl -sSI https://yourdomain.com > /dev/null && echo "chain verifies"

curl verifies against the system trust store and fetches nothing on its own behalf, so if it is satisfied the chain is genuinely complete - and every browser will be too.

Questions people actually ask