SSL certificate errors and how to fix them

What a name mismatch, an expired or self-signed certificate, and an incomplete or untrusted chain mean, and how to fix each one.

A certificate lets a browser check two things before it shows your site: that the server really answers for the name in the address bar, and that a trusted authority vouched for it. When either check fails, browsers show a full-page warning instead of the site, and apps refuse to connect.

Most certificate errors come down to the wrong name, the wrong dates, or a chain that doesn’t lead to a trusted authority. This page explains each one and how to fix it.

What SignalDNS checks

We look up the name, connect to its first public address (IPv4 first) on port 443, and send the name in the handshake, as browsers do. We complete a TLS 1.2 or 1.3 handshake, read the certificates the server sends, and send nothing else. An http:// address is checked on port 443 too.

Three results are checked separately, so one failure doesn’t hide another:

  • Hostname: whether the certificate covers the name, by its listed names and the wildcard rules.
  • Chain: whether it leads to a trusted root. We use the copy of Mozilla’s root store bundled with Node.js, and the report names it with the Node and OpenSSL versions.
  • Dates: whether today, by our server’s clock, falls between the certificate’s start and end dates.

The “Is the connection secure?” answer names the first that fails, in the order name, dates, chain. The details under it show all three.

No secure connection could be made

We reached the address but couldn’t complete a handshake with TLS 1.2 or later, or the server sent no certificate. Browsers show a connection error rather than a certificate warning. This is a problem.

The usual causes:

  • Nothing serves HTTPS on port 443. The port is closed, or the server speaks plain HTTP there.
  • The server has no certificate for this name and drops the connection, as nginx’s ssl_reject_handshake does.
  • A CDN or load balancer hasn’t been set up for the name yet.
  • It only accepts TLS 1.0 or 1.1. We say so separately; see TLS versions.

How to fix it: check that a certificate for this exact name is installed and that the HTTPS server block or virtual host lists the name. To see the handshake from another machine, run:

openssl s_client -connect example.com:443 -servername example.com </dev/null

If you use a CDN, check the domain’s certificate status in its dashboard.

The certificate doesn’t match the name

The certificate doesn’t list the name you checked. The answer shows the names it does cover. Browsers show a security warning. This is a problem.

A certificate covers the names in its Subject Alternative Name list. A wildcard such as *.example.com covers exactly one level: www.example.com and shop.example.com, but not example.com or a.b.example.com.

The usual causes:

  • The certificate covers www.example.com but not example.com, or the other way round.
  • A new subdomain was added without adding it to the certificate.
  • DNS points the name at a server or CDN that isn’t set up for it, so it sends a default certificate for another name.

How to fix it: get a certificate that lists every name you serve. With Certbot, name each one with -d:

sudo certbot --nginx -d example.com -d www.example.com

If your host or CDN issues certificates, add the name as a domain in its dashboard. If nobody should use the name, remove its DNS record. A redirect can’t help, because the warning appears before any redirect.

The certificate has expired

Its end date has passed. Browsers show a warning instead of the page, and apps refuse to connect. This is a problem.

Certificates last weeks or months, so they’re renewed again and again. An expired one usually means renewal stopped working, or a new certificate was issued but the server is still using the old one.

How to fix it:

  1. Renew now, in your host’s dashboard or with your ACME client, such as sudo certbot renew.
  2. Reload the web server so it loads the new file, such as sudo systemctl reload nginx.
  3. If several servers serve the site, install it on each.
  4. Find out why renewal stopped. See Certificate renewal.

The certificate isn’t valid yet

The certificate’s start date is in the future by our server’s clock. Browsers judge this by their own clock, so most visitors will see a warning until then. This is a problem.

It happens when a certificate is installed before its start date, or made on a machine whose clock was wrong. If we find it valid but one visitor sees “not valid yet”, the visitor’s device clock is probably wrong.

How to fix it: check the “Valid from” date in the certificate details. If it’s wrong, issue a new certificate. If it’s right, keep serving the previous certificate until then.

The certificate chain is incomplete

Your certificate is issued by an intermediate certificate, which is issued by a trusted root. The server should send its own certificate followed by the intermediate. This one sends only its own. This is a problem.

Most browsers cope: they often have the intermediate saved from other sites. Apps, scripts, command-line tools, and some devices don’t, and fail to connect. That’s why “Does it load?” says “Probably in browsers, but not in apps.” Our own request is one that fails.

How to fix it: configure the server to send the full chain, your certificate first.

  • Certbot: use fullchain.pem, not cert.pem. nginx takes it in ssl_certificate, and Apache 2.4.8 or later in SSLCertificateFile.
  • Other authorities: append the chain or bundle file they give you after your certificate, and use the result:
cat example.com.crt bundle.crt > example.com.chained.crt

To see what the server sends, add -showcerts to the openssl s_client command above.

The certificate is self-signed

The certificate is signed by its own key, not by a certificate authority, so nothing vouches for it. Browsers show a warning, and we never treat a self-signed certificate as trusted. This is a problem.

Many web servers and devices create one during setup as a placeholder. It stays until someone installs a real certificate, or it shows up when the real one isn’t set up for this name.

How to fix it: get a certificate from a public authority. Most hosts issue one at no cost when you add a domain, and Let’s Encrypt issues them at no cost through ACME clients such as Certbot.

An intermediate certificate has expired

Your own certificate is current, but an intermediate the server sends with it has expired. Some browsers find another path to a trusted root; many apps and older devices fail. This is a problem.

Authorities replace their intermediates from time to time. A chain file saved long ago can still hold the old one.

How to fix it: replace the chain file with the one your authority issues now, or install the full chain file that came with your latest certificate. Let’s Encrypt lists its current intermediates on its Chains of Trust page.

The certificate isn’t trusted

The chain doesn’t lead to a root in Mozilla’s root store, the one we use. The answer gives the reason code in brackets, such as UNABLE_TO_GET_ISSUER_CERT_LOCALLY or SELF_SIGNED_CERT_IN_CHAIN. Browsers will likely show a warning. This is a problem.

The usual causes:

  • A private or company authority issued it, for internal use. Company devices may trust it; nobody else does.
  • An authority Mozilla no longer trusts issued it.

How to fix it: use a certificate from a public authority, and install the full chain, including intermediates.

Your provider’s guides

What we can’t tell from outside

We check the first public address only, from one server. If the name has several addresses, or several servers behind a load balancer, others may send a different certificate. We don’t check revocation or certificate transparency, and we don’t apply each browser’s own trust rules. Other browsers and operating systems keep root stores of their own, which can differ slightly from Mozilla’s. The chain we show comes from our TLS library and can include a root from our store that the server didn’t send.

Sources

Last reviewed .