Home › Web & HTTP

ERR_SSL_PROTOCOL_ERROR — how to fix

This site can't provide a secure connection
localhost sent an invalid response.
ERR_SSL_PROTOCOL_ERROR

Quick fix: If it's localhost or a dev server, use http:// instead of https:// (or enable HTTPS in the dev server). Clear HSTS for the domain at chrome://net-internals/#hsts. On a real server, test with openssl s_client -connect example.com:443 -servername example.com and make sure the 443 server block has listen 443 ssl; and a valid certificate.

What it means

Chrome (and Edge) show ERR_SSL_PROTOCOL_ERROR when the TLS handshake can't complete because the server's reply doesn't follow the protocol. It is not a certificate trust problem (that's NET::ERR_CERT_AUTHORITY_INVALID); the connection never got far enough to check the certificate.

Quick test from a terminal:

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

wrong version number in the output means the server is speaking plain HTTP on that port.

Cause 1: https:// to an HTTP-only port (usually localhost)

https://localhost:3000 against a dev server that only serves HTTP produces exactly this error, with "localhost sent an invalid response".

Fix 1: use http, or enable HTTPS in the dev server

http://localhost:3000

If you need HTTPS locally, enable it explicitly, e.g. Vite with @vitejs/plugin-basic-ssl, next dev --experimental-https, or create a trusted cert with mkcert:

mkcert -install
mkcert localhost 127.0.0.1

Cause 2: HSTS forces https for the domain

If you once visited the domain (or localhost) over HTTPS with an Strict-Transport-Security header, Chrome silently upgrades every request to https, even when you type http://. Domains like .dev and .app are HSTS-preloaded and always require HTTPS.

Fix 2: delete the HSTS entry

Open chrome://net-internals/#hsts, enter the domain under "Delete domain security policies" and click Delete. For local development, use localhost or .test names instead of .dev.

Cause 3: server listens on 443 without SSL enabled

A common nginx mistake:

server {
    listen 443;          # wrong: plain HTTP on port 443
    server_name example.com;
}

Fix 3: configure TLS on the 443 listener

server {
    listen 443 ssl;
    http2 on;
    server_name example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}
sudo nginx -t && sudo systemctl reload nginx

Apache: make sure mod_ssl is enabled (sudo a2enmod ssl) and the <VirtualHost *:443> has SSLEngine on.

Cause 4: no matching TLS version or cipher

Old servers that only support TLS 1.0/1.1, or a config with ssl_protocols TLSv1;, fail in modern browsers. Check what the server supports:

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

Use ssl_protocols TLSv1.2 TLSv1.3;.

Cause 5: something in the middle

Antivirus "HTTPS scanning", corporate proxies, captive portals on public Wi‑Fi, or a wrong system clock can break the handshake. Try another network or browser profile, disable HTTPS inspection temporarily, and check that the date and time are correct.

Last updated 2026-11-04.