# ERR_SSL_PROTOCOL_ERROR: what it means and how to fix it

> ERR_SSL_PROTOCOL_ERROR means the browser and the server could not agree on a secure connection, so the handshake stopped before any page loaded. Here is the quick fix for visitors and the server-side causes for site owners.

Updated 2026-09-26 · Security · HTML version: https://getreport.app/guides/err-ssl-protocol-error

ERR_SSL_PROTOCOL_ERROR is Chrome's message when the browser tried to open a secure HTTPS connection and the server answered with something that is not a valid TLS handshake, so the connection stopped before any page loaded. The page above it reads "This site can't provide a secure connection" and usually "example.com sent an invalid response". Most of the time the cause is on the server: port 443 is not set up for HTTPS. Sometimes it is on the visitor's device. This guide gives the two-minute visitor fix first, then the server-side causes and fixes. For how certificates and the TLS handshake work in general, start with [what an SSL certificate is and how HTTPS works](https://getreport.app/guides/ssl-tls-checks-that-matter).

## Quick answer

- **Visitor**: try the site in a private window and on mobile data. If it works there, check your device's date and time, turn off HTTPS scanning in your antivirus or VPN, and clear the browser's cache. If it fails everywhere, the site is broken and only its owner can fix it.
- **Site owner**: the usual cause is a server that speaks plain HTTP on port 443, or has no certificate for this host name. Test with `openssl s_client -connect example.com:443 -servername example.com`.
- In nginx, `listen 443;` without `ssl` is the classic mistake: it must be `listen 443 ssl;` with a certificate.
- Behind a CDN or load balancer, make sure the host name is actually added there and has an edge certificate.
- After the fix, run the [SSL checker](https://getreport.app/tools/ssl-check) to confirm the handshake, the certificate and the redirect.

## What ERR_SSL_PROTOCOL_ERROR means

Every `https://` page starts with a TLS handshake: the browser says hello, the server answers with its certificate, and both agree on keys. ERR_SSL_PROTOCOL_ERROR (Chrome's network error -107) means that exchange broke at the protocol level. The browser did not reject a certificate; it never got a usable one. The server sent bytes that are not TLS, closed the connection halfway, or sent a TLS alert the browser could not recover from.

That distinction tells you where to look:

| What the browser shows | What went wrong |
| --- | --- |
| ERR_SSL_PROTOCOL_ERROR, "sent an invalid response" | The handshake itself failed: no TLS on the port, a broken configuration, something in the middle rewriting traffic |
| ERR_SSL_VERSION_OR_CIPHER_MISMATCH, "uses an unsupported protocol" | TLS works, but the server only offers versions or ciphers the browser has dropped, such as TLS 1.0 |
| "Your connection is not private" with a `NET::ERR_CERT_…` code | The handshake worked, but the certificate is expired, for another name, or untrusted. See [fixing "Your connection is not private"](https://getreport.app/guides/your-connection-is-not-private) |
| Cloudflare 525 or 526 page | Your visitor reached Cloudflare fine; Cloudflare could not connect securely to your server. See [Cloudflare errors 525 and 526](https://getreport.app/guides/cloudflare-ssl-errors-525-526) |

Other browsers name the same failure differently. Firefox shows "Secure Connection Failed" with a code such as `SSL_ERROR_RX_RECORD_TOO_LONG`, which almost always means plain HTTP on port 443. Safari says it "can't establish a secure connection to the server". Edge uses Chrome's wording.

## If you're a visitor: the quick fix

If the error appears on one site only, the site is the likely cause, but a few things on your side produce the same message. Try these in order and stop when the page loads.

1. **Open the site in a private window, and on your phone over mobile data.** If it works there, the problem is your browser or your network. If it fails everywhere, the site is broken; tell the owner and wait.
2. **Check your device's date and time.** Set them to automatic. A clock that is days off breaks certificate checks and can break the handshake.
3. **Turn off HTTPS scanning in antivirus software and pause VPNs or proxy extensions.** Security suites that inspect encrypted traffic sit in the middle of the handshake, and an outdated one can break it. If the site then works, update the software or add an exception.
4. **Clear cached data for the site.** In Chrome: Settings → Privacy and security → Delete browsing data, "Cached images and files". On Windows, the old "Clear SSL state" button in Internet Options also clears cached certificates.
5. **Try another network.** Company, school and hotel networks sometimes intercept HTTPS; a captive login page can too until you have signed in.

Avoid advice to turn on old TLS versions or disable security features in the browser's flags. Chrome removed those switches, and weakening your browser to reach one site is a bad trade.

> **Note:**
> If you run the site yourself and see this on `localhost` or a `.dev` or `.app` domain, the browser is probably forcing HTTPS on a server that only speaks HTTP. `.dev` and `.app` are on the HSTS preload list, so browsers always use HTTPS for them; serve those domains over HTTPS, or use `http://localhost` instead.

## If you own the site: find the cause

Start with a test from outside your network, so a local cache or VPN does not hide the problem.

> **Free tool:** [SSL checker: check your SSL certificate and TLS](https://getreport.app/tools/ssl-check): Free SSL checker: test certificate expiry and chain, TLS versions, HTTPS redirects, HSTS and mixed content on any site, with the fix for each. No sign-up.

The SSL checker connects like a strict client. When the HTTPS handshake fails, a report cannot load the page over `https://` at all. If you typed the address without `https://`, getReport then tries `http://` once: if that works, the report shows the site as served over HTTP, and if `http://` redirects back to the broken `https://` address, the report stops with "We could not reach that site". Either result points at the HTTPS setup, and these findings come back green once it works:

> **Check: The site is served over HTTPS.** Browsers label HTTP pages "Not secure" and Google uses HTTPS as a small ranking signal. Anyone on the network can read or alter what visitors see.
>
> 1. Get a certificate (Let's Encrypt is free) and enable HTTPS on your host.
> 2. Redirect every http:// URL to https:// with a 301.

> **Check: http:// redirects to https://.** Visitors who type your address without https, or follow an old link, land on the unencrypted page. Every one of those visits can be read or altered on the network.
>
> 1. Redirect every http:// URL to the same path on https:// with a 301.
> 2. nginx: return 301 https://$host$request_uri; Apache: RewriteRule in .htaccess; most hosts and Cloudflare have a "Always use HTTPS" switch.

For the exact failure, use OpenSSL (on macOS and Linux it is built in; on Windows, Git Bash includes it):

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

Read the output like this:

- **`wrong version number`** or **`packet length too long`**: the server answered in plain HTTP. Port 443 is not configured for TLS.
- **`Connection refused`** or a timeout: nothing listens on 443, or a firewall blocks it. Browsers usually show `ERR_CONNECTION_REFUSED` or `ERR_CONNECTION_TIMED_OUT` for this, not the protocol error.
- **`alert unrecognized name`** or **`handshake failure`** before any certificate appears: the server has no certificate for this host name, or refuses the name you sent in SNI.
- **A certificate chain and `Verify return code: 0 (ok)`**: the handshake works from here. The problem is between the visitor and your server: a CDN, a firewall that inspects traffic, or the visitor's device.

Also try `curl -vI https://example.com`, which prints the same handshake steps with plain-language errors.

## Server-side causes and fixes

### Port 443 speaks plain HTTP

The most common cause. The web server listens on 443 but without TLS, so it answers the browser's hello with an HTTP response.

In nginx, `ssl` is missing from the `listen` line:

```nginx
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com www.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
}
```

In Apache, the `<VirtualHost *:443>` block needs `SSLEngine on` and the certificate lines, and `mod_ssl` must be enabled (`sudo a2enmod ssl` on Debian and Ubuntu). A common slip is a `*:443` virtual host copied from the port 80 one without those lines.

In Node.js, Docker or an app server, the app often listens on 443 with plain HTTP because TLS was meant to happen in a reverse proxy in front of it. Put the proxy back in front, or terminate TLS in the app.

Reload after every change and test the configuration first: `sudo nginx -t && sudo systemctl reload nginx`, or `sudo apachectl configtest && sudo systemctl reload apache2`.

### No certificate for this host name

A server with many sites picks the certificate by the name the browser sends (SNI). If `www.example.com` or a new subdomain has no matching `server` block or virtual host, the request falls through to a default one. Depending on how that default is set up, the browser gets another site's certificate ("Your connection is not private") or no certificate at all (the protocol error). nginx's `ssl_reject_handshake on;` in a default block, for example, deliberately refuses unknown names.

Fix it by adding the name to the site's configuration and issuing a certificate that covers it. With Let's Encrypt this is one command, covered in [getting a free certificate with Let's Encrypt](https://getreport.app/guides/free-ssl-certificate-lets-encrypt).

### A broken certificate or key file

If the certificate and private key do not match, or a file is empty or truncated after a failed renewal, nginx or Apache usually refuses to start. Some servers and panels start anyway and fail every handshake. Check that the two belong together; the outputs must be identical:

```bash
openssl x509 -noout -pubkey -in fullchain.pem | openssl sha256
openssl pkey -pubout -in privkey.pem | openssl sha256
```

### Only old protocols on the server

A server that only accepts TLS 1.0 or 1.1 shows `ERR_SSL_VERSION_OR_CIPHER_MISMATCH` in current browsers rather than the protocol error, but the fix is in the same place: allow TLS 1.2 and 1.3. The finding below flags a server that still accepts the old versions:

> **Check: The server only accepts TLS 1.2 or newer.** TLS 1.0 and 1.1 have known weaknesses and every major browser dropped them in 2020. A server that still accepts them lets an attacker downgrade a connection to one they can break.
>
> 1. Set the minimum protocol to TLS 1.2 (nginx: ssl_protocols TLSv1.2 TLSv1.3; Apache: SSLProtocol -all +TLSv1.2 +TLSv1.3).
> 2. Behind Cloudflare or another CDN, set "Minimum TLS Version" to 1.2 in its dashboard.

### A CDN, load balancer or firewall in the middle

With a CDN or cloud load balancer in front, the visitor's handshake happens there. If the host name is not added to the CDN, or its edge certificate is still being issued (usually minutes after adding a domain), visitors can get a protocol error while your server is fine. Check the CDN's dashboard for the certificate status of the exact name. Behind Cloudflare, the errors between Cloudflare and your server show as 525 or 526 instead.

A web application firewall or intrusion prevention box that inspects TLS can also break handshakes, especially with TLS 1.3. Test from a network that bypasses it.

### HSTS on a site that dropped HTTPS

If your site once sent `Strict-Transport-Security`, browsers that saw it will only use HTTPS for your domain until `max-age` runs out, often a year. Moving to a host without HTTPS then breaks those visitors with a protocol or certificate error, and there is no way to undo it from the server. The only fix is to serve HTTPS again. [HSTS: enabling it safely](https://getreport.app/guides/hsts-safely-and-the-preload-list) covers why you set it only once HTTPS is stable everywhere.

## How to verify the fix

1. `openssl s_client -connect example.com:443 -servername example.com` shows your certificate and `Verify return code: 0 (ok)`.
2. Repeat for every name visitors use: `example.com`, `www.example.com` and any subdomain.
3. Open the site in a private window on desktop and on a phone over mobile data.
4. Run the [SSL checker](https://getreport.app/tools/ssl-check): the HTTPS, redirect, chain and TLS version findings should pass, with 30 or more days left on the certificate.
5. Check the redirect: `http://example.com` should reach `https://example.com` in one 301.

## Common mistakes

- **`listen 443;` without `ssl`** in nginx. Everything looks configured, and every visitor gets the protocol error.
- **Adding a new subdomain to DNS but not to the web server or certificate.** The name reaches the server and falls through to the wrong site.
- **Pointing a domain at a new host before its certificate is issued.** Issue the certificate first, or accept a few minutes of errors after the DNS switch.
- **Telling visitors to clear their cache when the server is broken.** If the openssl test fails from outside, no visitor-side fix will help.
- **Turning HSTS on with a long `max-age` before HTTPS works on every subdomain.**

## Questions people ask

### How do I fix ERR_SSL_PROTOCOL_ERROR?

First find out whose side it is on. If the site fails in a private window, on another device and on mobile data, the server is the cause: its port 443 is not serving TLS for that host name, so the owner has to fix the web server or CDN configuration. If it works elsewhere, fix your device: set the clock to automatic, turn off HTTPS scanning in antivirus software or VPNs, and clear the browser's cached files.

### What does "This site can't provide a secure connection" mean?

It means Chrome tried to open the page over HTTPS and the secure handshake with the server failed, so it showed nothing rather than an unprotected page. The line under it, usually "sent an invalid response" with ERR_SSL_PROTOCOL_ERROR, says the server did not answer in TLS. It is not a warning you can click through: the site has to serve HTTPS properly, or your device must stop interfering with the connection.

### Can a website owner cause ERR_SSL_PROTOCOL_ERROR?

Yes, and usually they do. The most common causes are a server that answers plain HTTP on port 443, a host name with no certificate configured, a mismatched certificate and key after a renewal, or a CDN that has not issued its certificate for the domain yet. An `openssl s_client` test from outside shows which one, and the SSL checker confirms the fix.

### Why does ERR_SSL_PROTOCOL_ERROR appear on localhost?

Because the browser asked for `https://localhost` and your development server only speaks plain HTTP. That happens when you type `https://`, when an app redirects to HTTPS, or when an earlier HTTPS project on localhost set HSTS. Open `http://localhost:port` instead, remove the redirect in development, or delete the domain's security policy at `chrome://net-internals/#hsts` if HSTS was cached.

### Is ERR_SSL_PROTOCOL_ERROR dangerous?

No, not in itself: it means the browser refused to load a page because it could not set up encryption, which protects you. It does not mean you were attacked. On a network you do not trust, such as public Wi-Fi, do not try to work around it with another browser or an old protocol; wait until you are on a known network or the site owner has fixed it.
