Skip to content
← All articles

HTTPS Not Working: Causes, Checks and How to Fix It

Short answer. HTTPS breaks for two very different classes of reasons, and they need opposite fixes. If the site fails only for you, the usual culprits are the device clock, antivirus TLS interception, a corporate proxy or browser state. If it fails for everyone, the fault is on the server: an expired certificate, an incomplete chain, a wrong hostname, a closed port 443 or a redirect loop. Start with that fork.

This article is a router. It does not chase every error down to the last config flag — the common ones have their own detailed write-ups, and you will get a link when you reach them. The goal here is narrower: work out where the fault lives in a few minutes, so you do not spend an evening tuning a server when the real problem is a laptop clock that drifted.

Start here: is it broken for everyone or only for you

This is the one question to answer before you open a web server config. It splits everything that follows into two branches that never meet. Getting it wrong is the most expensive mistake in the whole process: people reissue perfectly good certificates because their office machine sits behind a gateway that inspects traffic.

Four ways to get the answer, fastest first:

  • Mobile data. Turn off Wi-Fi on your phone and open the site over the cellular network. That is a different path, a different DNS resolver, a different device and a different root certificate store. If it works there, the fault is in your network or on your computer.
  • Private window in a different browser. Removes the influence of cache, some extensions and stored HSTS entries. Does not remove the influence of antivirus software or the system clock.
  • A different device on a different network. The most honest local test: every variable changes except the site itself.
  • An external check. Run the SSL certificate check and see the site through a third-party server. The tool sees exactly what your server sends, without your antivirus, proxy or clock in the way.
What you observeMost likelyWhere to look
External check shows a valid certificate, you get an errorLocal causeClock, antivirus, proxy, cache
External check complains tooServer-side causeCertificate, chain, hostname, port
Fails on every device in one network but works over mobile dataNetwork or gatewayProxy, inspection, DNS, filtering
Fails in one browser onlyBrowser stateCache, extensions, stored HSTS entry
Error comes and goes between reloadsLoad balancingBackends holding different certificates
That last row is the underrated one. If several servers sit behind one domain and the certificate was renewed on some of them, the site breaks every other request, and a single external check can easily come back green. Run the check several times in a row.
Diagram of the diagnostic fork: testing a site over mobile data, in another browser and from an external server leads to two branches of HTTPS troubleshooting
The everyone-or-just-me fork decides everything that follows.

What the browser is telling you: error codes decoded

The browser almost always names the cause — just not in plain language. The error code is on the warning page: in Chrome you expand it with the advanced button, in Firefox it sits in the details block. Below are the codes you will actually meet, and where each of them leads.

CodeWhat actually happenedEveryone or just youWhere to go next
NET::ERR_CERT_DATE_INVALIDCurrent time falls outside the certificate validity window. Either the certificate expired or the client clock is wrongEitherExpired certificate
NET::ERR_CERT_AUTHORITY_INVALIDThe chain could not be built up to a trusted root: intermediates missing, self-signed certificate, or interception on the pathEitherError breakdown, incomplete chain
NET::ERR_CERT_COMMON_NAME_INVALIDThe hostname you typed is not in the certificate name list (SAN)EveryoneHostname section below
NET::ERR_CERT_REVOKEDThe certificate authority revoked the certificate before its expiry dateEveryoneReissue, investigate the revocation
ERR_SSL_VERSION_OR_CIPHER_MISMATCHClient and server share no TLS version and no cipher suiteEveryone or older clientsVersion or cipher mismatch
ERR_SSL_PROTOCOL_ERRORThe handshake broke: the server answered with something that is not TLS, dropped the connection or returned garbageEveryoneError breakdown, handshake failure
ERR_CONNECTION_REFUSED / ERR_CONNECTION_TIMED_OUTNothing reached port 443: nobody is listening, or a filter drops the packetsDepends where the filter sitsChecking open ports
ERR_TOO_MANY_REDIRECTSA redirect loop, most often HTTP to HTTPS and backEveryoneToo many redirects
SEC_ERROR_EXPIRED_CERTIFICATE (Firefox)Same meaning as ERR_CERT_DATE_INVALIDEitherExpired certificate
SEC_ERROR_UNKNOWN_ISSUER (Firefox)Unknown issuer, most often because intermediate certificates are not being sentEitherIncomplete chain
MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT (Firefox)The certificate signs itself, there is no trusted issuer above itEveryoneSelf-signed certificate
MOZILLA_PKIX_ERROR_MITM_DETECTED (Firefox)Firefox recognised traffic interception by antivirus software or a gatewayJust youAntivirus and proxy section
SSL_ERROR_NO_CYPHER_OVERLAP (Firefox)The Firefox equivalent of a cipher mismatchEveryone or older clientsVersion or cipher mismatch

Safari shows no codes at all — you only get "This Connection Is Not Private". When that happens, open the same address in Chrome or Firefox: you need the code so you are not guessing. A wider catalogue of SSL messages lives in the SSL errors section.

MOZILLA_PKIX_ERROR_MITM_DETECTED is the most useful message on the list. Firefox is not merely saying it does not trust the certificate — it is stating that something is decrypting the connection along the way. No web server config fixes that. The fix lives on the client.

Broken only for you: clock, antivirus, proxy

A device clock that drifted

The most common and least obvious cause. The mechanism is simple but rarely spelled out: a certificate carries two fields, notBefore and notAfter. During validation the client takes its own system time and checks whether it falls inside that window. There is no lookup against an authoritative time source — the comparison runs against your device clock.

That gives two symmetric failures. A clock months behind makes a freshly issued certificate "not valid yet". A clock running ahead makes it "already expired". The browser reports both the same way, as a date error. Let's Encrypt certificates are valid for roughly three months by default (Let's Encrypt documentation), so the validity window is narrow and clock drift shows up almost immediately.

Classic sources: a dead CMOS battery in an older desktop, where the date falls back to the distant past after every power-off; a virtual machine resumed after a long pause; a device with no time synchronisation on an isolated network; a date edited by hand to work around a licence check.

# Linux: what the system thinks, and whether time is synchronised
date -u
timedatectl status

# Windows (elevated command prompt)
w32tm /query /status

# macOS: current time in UTC
date -u

On Linux, look in the timedatectl status output for the system clock line and the synchronisation line — if synchronisation is off, turn it on and retry the site. On macOS, automatic time is enabled in the date and time system settings. On Windows, force a resync after enabling it and reopen the browser.

Checking expiry with local tools inherits exactly the same flaw. The openssl x509 -checkend command compares certificate dates against the clock of the machine it runs on. If that clock is wrong, the command is wrong with it. This is precisely why the first check should come from an external service rather than your own terminal.

Antivirus software and corporate TLS interception

Many antivirus products and nearly all corporate security gateways can look inside HTTPS. There is only one way to do that: terminate the connection at the inspection point and hand you their own certificate, issued by their private certificate authority. It works for as long as their root sits in your trust store and the browser actually consults that store.

It can break in three places. The root failed to install after a system reinstall. The browser keeps its own trust store and never looks at the system one. The site pins its keys, so substitution is refused by design. The result is an untrusted-issuer error even though the site certificate is perfectly fine.

One step settles it: look at who issued the certificate. A public certificate authority in the issuer field means no interception. Your antivirus product name, a gateway vendor name or something like "local root" means you are not looking at the site certificate at all.

# Who issued the certificate that actually reaches this machine
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer

# Typical healthy output for a public site:
# subject=CN=example.com
# issuer=C=US, O=SSL Corporation, CN=Cloudflare TLS Issuing ECC CA 3

Compare that with what the external certificate check reports. Two different issuers for one domain is direct proof of interception on your side. From there the decision is administrative: trust the gateway root, exempt the domain from inspection, or temporarily disable HTTPS scanning in the antivirus to confirm the diagnosis.

Browser cache, stored HSTS entries and an ageing root store

A browser remembers more about a site than you would expect. It caches not only pages but the decision that "this domain is HTTPS-only" — the HSTS entry. If you fixed the server and the browser still behaves the old way, that state has to be cleared. Chrome exposes an internal page at chrome://net-internals/#hsts where a domain can be deleted from the security policy list. Clearing cache and cookies for all time handles most of the rest.

A separate category is devices that stopped receiving updates. The root certificate store ships with the system; once updates stop, the device sooner or later fails to recognise issuers that appeared after the last update. The symptom is distinctive: current devices open the site fine, while an old phone or a long-unpatched computer throws untrusted-issuer errors across half the internet at once. The only real fix is updating the system.

Diagram of TLS interception: antivirus software or a corporate gateway replaces the site certificate with one issued by a local certificate authority
Under TLS interception the browser never sees the site certificate, only the middlebox one.

Broken for everyone: the certificate itself

The certificate expired

The simplest cause and the most annoying one: automatic renewal broke, the expiry notice went to spam, and one night the site stopped opening for everyone at once. One command settles it — the notAfter field is the deadline.

An incomplete chain

The case that looks like witchcraft: "it opens in my Chrome but not on my phone and not in curl". Here is the mechanism. The server must send not only the site certificate but the intermediate certificates that link it to a root. If it does not, the client has to complete the chain on its own. Some clients can — they fetch the missing link using a pointer inside the certificate and then cache it. Others cannot do it at all.

Hence the signature symptom: the site works for people who already visited another site from the same issuer, and fails for everybody else. Counting the links is easiest by counting certificate blocks in the server response.

# How many certificates the server actually sends
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null \
  | grep -c "BEGIN CERTIFICATE"

# 1  — incomplete chain, only the site certificate was sent
# 2+ — intermediates are present, which is the healthy case

A count of one is almost always the diagnosis. The fix is assembling the full chain file on the web server; in nginx the certificate directive points at a file where intermediates are appended after the site certificate (nginx documentation). The full walkthrough is in the article on an incomplete certificate chain.

The certificate name does not match the domain

A certificate is not valid "for a server" — it is valid for a specific list of names, stored in the SAN extension. Modern browsers check only that list and ignore the legacy CN field, so a certificate issued for example.com will not cover www.example.com unless the second name is in the list. The matching rules are defined in RFC 6125.

There is a trap here that catches experienced administrators too: by default openssl s_client does not verify the hostname at all. It will confirm chain trust and return a zero code while the browser shows a name error on the same address. To make the check match browser behaviour you need an explicit flag.

# The list of names the certificate is valid for
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -ext subjectAltName

# Hostname verification the way a browser does it
echo | openssl s_client -connect example.com:443 -servername example.com \
  -verify_hostname example.com 2>&1 | grep "Verify return code"

# On a name mismatch the output reads:
# Verify return code: 62 (hostname mismatch)

The address serves another site's certificate

When several sites share one IP address, the server decides whose certificate to send from the name the client announces at the start of the handshake — the SNI mechanism (RFC 6066). If your domain has no virtual host of its own, or that host names no certificate, the server falls back to the default site's certificate — someone else's. The browser honestly reports a name mismatch, even though a valid certificate for your domain is sitting right there on disk.

# What is served when no name is announced (the default site)
echo | openssl s_client -connect 203.0.113.10:443 2>/dev/null \
  | openssl x509 -noout -subject

# What is served when the name is announced explicitly
echo | openssl s_client -connect 203.0.113.10:443 -servername shop.example.com 2>/dev/null \
  | openssl x509 -noout -subject

Different values from the two commands are normal and mean SNI is working. Identical values, both carrying a foreign name, mean the virtual host for your domain never took effect. The address 203.0.113.10 comes from a range reserved for documentation (RFC 5737) — substitute your own.

Diagram of SNI certificate selection: the client sends the domain name and the server returns the matching certificate or the default site certificate
Without SNI or without a virtual host the server falls back to the default site certificate.

Broken for everyone: the certificate is fine, the site still fails

Port 443 is closed or the wrong process is listening

Validation does not always get a turn. If port 443 does not answer, the browser reports a refused connection or a timeout and certificates never enter the picture. Typical causes: the web server listens on port 80 only, a firewall rule was never opened, the provider or hosting blocks the port, the service died after a reboot.

# Does the port answer from outside
nc -z -w 5 example.com 443 && echo "443 open" || echo "443 closed"

# What is listening on 443 on the server itself (Linux)
ss -ltnp | grep ':443'

# Connection diagnostics end to end
curl -sS --connect-timeout 5 -o /dev/null -w '%{http_code}\n' https://example.com

If curl returns a class-seven failure (could not connect), the problem is network-level, not certificate-level. A quick outside-in check is the port scanner, no server access required.

An HTTP to HTTPS redirect loop

The pattern that breaks sites right after a load balancer or CDN is added. The edge node accepts HTTPS and talks to your server over plain HTTP. Your server sees an unencrypted request and dutifully redirects it to HTTPS. The edge accepts HTTPS again and again talks HTTP. The browser gives up with ERR_TOO_MANY_REDIRECTS while the certificate is flawless.

The key is the header the edge uses to announce the original request scheme. The redirect rule must inspect that header rather than the local connection scheme. The same class of failure comes from a "always add www" rule living next to an "always strip www" rule elsewhere in the config.

# The full redirect chain and the final address
curl -sSIL -o /dev/null \
  -w 'redirects=%{num_redirects} final=%{url_effective} code=%{http_code}\n' \
  http://example.com

# Step by step, with status codes and Location headers
curl -sSIL http://example.com | grep -iE '^HTTP/|^location:'

More than three or four hops is already worth investigating. The redirect checker shows the chain visually, and the specific cases are covered in the article on too many redirects.

Mixed content

A special flavour of "HTTPS is broken" is when it formally works. The page loads over HTTPS while its images, scripts and stylesheets are referenced over HTTP. The browser blocks the active resources, the padlock disappears or turns into a warning, the layout falls apart and buttons stop responding. Users describe this in exactly these words: "the site stopped working after we moved to HTTPS".

Finding those references by hand is painful: some addresses come from the database, some from themes and plugins, some from third-party widgets. It is easier to run the page through the mixed content check. The step-by-step fix is in the article on mixed content.

HSTS after a rollback to HTTP

HSTS (RFC 6797) works like this: the server sends a header with a duration once, and for that entire period the browser remembers that this domain is reachable over HTTPS only. It remembers it strictly — it does not merely redirect, it refuses to open HTTP and shows no "proceed anyway" button.

Hence a scenario that looks like a disaster. A site moves to HTTPS, the header goes out with a long duration, something goes wrong and HTTPS gets rolled back. New visitors are fine. Everyone who visited before loses the domain entirely — until the remembered period runs out or the entry is cleared by hand in every browser.

# Does the server send an HSTS header, and for how long
curl -sSI https://example.com | grep -i 'strict-transport-security'

# A real response looks like this:
# strict-transport-security: max-age=31536000
The only genuine way out is to bring HTTPS back. Removing the header server-side is not enough: browsers that already received it keep honouring the old entry for the remaining period. If the domain made it into the preload list, removal takes months. Details are in the write-up on HSTS and the preload list.

How to check: commands that answer in a minute

Below is a run that covers nearly every case described above. Work through it in order and stop at the first step that returns something unexpected. Replace example.com with your own domain.

# 1. Does port 443 answer
nc -z -w 5 example.com 443 && echo "443 open" || echo "443 closed"

# 2. Who the certificate is for, who issued it, until when, and for which names
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

# 3. Does the system trust the chain, and does the hostname match
echo | openssl s_client -connect example.com:443 -servername example.com \
  -verify_hostname example.com 2>&1 | grep "Verify return code"

# 4. Is the chain complete (1 means incomplete)
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null \
  | grep -c "BEGIN CERTIFICATE"

# 5. Where the redirects lead
curl -sSIL -o /dev/null \
  -w 'redirects=%{num_redirects} final=%{url_effective} code=%{http_code}\n' \
  http://example.com

Step three carries the most information. The final verification code names the cause outright:

Verify return codeWhat it meansWhat to fix
0 (ok)Chain is trusted and the hostname matchedThe certificate is not your problem
10 (certificate has expired)Validity ended — or the clock on the machine running the command is wrongReissue, renewal automation, clock
18 (self-signed certificate)The certificate signs itselfObtain one from a public authority
19 (self-signed certificate in certificate chain)The chain root is untrusted: a private authority or interceptionInspect the issuer, look for interception
21 (unable to verify the first certificate)Intermediate certificates are not being sentAssemble the full chain
62 (hostname mismatch)The domain is not in the certificate name listAdd the name, check the virtual host
Do not reach for the flag that disables certificate validation just to "confirm the site is alive". With it the request always succeeds, so you get a false confirmation. That flag is good for exactly one thing: proving the failure is in validation rather than in the service itself.

How to check online, without a terminal

The value of an external check is not convenience but vantage point: it looks at the site from a third-party server, where your clock, antivirus, proxy and cache do not exist. That is why it belongs first — it closes half of the fork immediately.

Diagram of HTTPS diagnostics: external checks of the certificate, redirects, mixed content and port 443 combine into one result
An external check sees the site without your antivirus, proxy or system clock.

Frequently asked questions

Why does HTTPS fail on one computer only?

Because certificate validation happens entirely on the client. Three suspects, in order: the system clock, antivirus or gateway TLS interception, and stored browser state. Check the date, look at the certificate issuer, and compare it with the external check. Different issuers mean the traffic is being decrypted on your side.

Why did every site stop opening over HTTPS at once?

A simultaneous failure across all domains is never the sites' fault. That is what a drifted clock looks like, or a missing root from an intercepting gateway, or a device so far behind on updates that it does not recognise newer issuers. Start with date and time, then check whether an unfamiliar certificate authority appeared in the issuer field.

No, that is a different task. Questions like "how do I make a link, photo or video use HTTPS" are about inserting material into an article or a message so the address starts with https://. A broken HTTPS site is unrelated. If your question is about the protocol itself, start with the explainer on what HTTPS is. If material embedded over HTTP is breaking an HTTPS page, that is mixed content and has its own write-up.

Can I just click "Proceed anyway (unsafe)"?

On someone else's site, no. That button removes the one protection you have against somebody sitting between you and the server reading the traffic, passwords and cookies included. On your own site during debugging it is acceptable as a one-off test of a hypothesis. Note that with an active HSTS policy the button will not be there at all — the browser simply refuses the domain.

The site opens over HTTP but not over HTTPS. Why?

It means the service is alive and the HTTPS side specifically is unconfigured or broken. Check three things in order: whether the server listens on port 443, whether a virtual host with a certificate for this domain took effect, and whether the address is serving another site's certificate. Commands for all three are in the sections above.

Will reinstalling the browser help?

Usually not, and it costs you time. A reinstall does not touch the system clock, the root certificate store or antivirus settings — three of the four causes. The only sensible parts are clearing the cache and deleting the HSTS entry for that specific domain; everything else is fixed outside the browser.

Checklist

  • Answer the main question first: broken for everyone or only for you. Mobile data and an external check settle it in a minute.
  • Expand the error details and write down the code — it names the cause.
  • If the fault is local: check date and time, then the certificate issuer, then clear cache and the HSTS entry.
  • If the fault is server-side: check the validity window, chain completeness and the SAN name list.
  • Confirm the server sends more than one certificate — a count of one means an incomplete chain.
  • Verify the hostname with an explicit flag: without it openssl s_client returns "ok" where the browser shows an error.
  • Confirm port 443 is open and the right process is listening on it.
  • Trace the redirect chain — an HTTP to HTTPS loop produces an error unrelated to certificates.
  • Scan the page for mixed content if the site loads but misbehaves.
  • Never roll a site back to HTTP while HSTS is active — you will have no way to bring visitors back.
  • Put monitoring on the certificate validity window so the next incident does not start with a customer call.

Check your website right now

Check your site's SSL →
More articles: SSL/TLS
SSL/TLS
Expired SSL Certificate: How to Fix NET::ERR_CERT_DATE_INVALID
15.04.2026 · 690 views
SSL/TLS
SSL Certificate Chain: How It Works, How to Verify It and How to Fix an Incomplete Chain
15.04.2026 · 639 views
SSL/TLS
Weak Cipher Suites: How to Find and Disable Insecure TLS Ciphers
15.04.2026 · 609 views
SSL/TLS
SSL Handshake Failed: Root Causes and Step-by-Step Diagnosis
15.04.2026 · 607 views