How to Fix ERR_SSL_PROTOCOL_ERROR in Chrome
Tell a plain-HTTP port from a forced HTTPS upgrade or a filter, then apply the matching Chrome fix.
Updated
TL;DR
ERR_SSL_PROTOCOL_ERROR means Chrome opened a secure (TLS) handshake and the reply was not valid TLS, most often plain HTTP from a port that has no HTTPS. On a local or internal address, open it with http:// and the same port; a public site that fails everywhere needs its owner.
How Chrome and automation tools show ERR_SSL_PROTOCOL_ERROR
The error page names the host whose reply failed; Puppeteer, Playwright and ChromeDriver pass Chrome's net:: code on to your script.
| Where | What you see |
|---|---|
| Chrome desktop, https:// address on a plain-HTTP port | This site can't provide a secure connection. localhost sent an invalid response. ERR_SSL_PROTOCOL_ERROR |
| Chrome desktop, http://myapp.dev:3000 typed, then upgraded to https:// by the HSTS preload list | This site can't provide a secure connection. myapp.dev sent an invalid response. ERR_SSL_PROTOCOL_ERROR |
| Chrome DevTools Console, a script requested over https:// from a plain-HTTP port | Failed to load resource: net::ERR_SSL_PROTOCOL_ERROR |
| Puppeteer page.goto() | net::ERR_SSL_PROTOCOL_ERROR at https://localhost:3000/ |
| Playwright page.goto() | page.goto: net::ERR_SSL_PROTOCOL_ERROR at https://localhost:3000/ |
| Selenium with ChromeDriver, driver.get() | unknown error: net::ERR_SSL_PROTOCOL_ERROR |
Why this happens
Chrome expected an encrypted reply, but the server answered in plain, unencrypted text.
BoringSSL, Chrome's TLS code, expects a record whose version byte is 3, and the letters of an HTTP reply fail that test. Alerts with no Chrome code of their own, such as internal_error, land here too.
Diagnose your ERR_SSL_PROTOCOL_ERROR first
The first three need only a browser; the last two use Chrome DevTools.
Load the same address on another device on this Wi-Fi; if it works there, the cause sits in this computer's Chrome or proxy settings.
Open two unrelated https:// sites; if both fail with this code while http:// pages load, a proxy or filter is answering for every site.
Read the summary line; 'uses an unsupported protocol' with ERR_SSL_VERSION_OR_CIPHER_MISMATCH means the server speaks TLS but shares no version or cipher with Chrome.
In DevTools Network, a 307 Internal Redirect with Non-Authoritative-Reason: HSTS means Chrome rewrote http:// to https:// before connecting.
If the page opens with parts missing, check the DevTools Console; Failed to load resource: net::ERR_SSL_PROTOCOL_ERROR names each file that failed this way.
Solutions ranked by effectiveness
Start with the address you typed; the last fix is for whoever runs the site.
- Most common fix
Open plain-HTTP ports with http://
Applies to local or internal addresses with a port, such as :3000 or :8080, served without a certificate. Chrome uses the scheme in the address or link, whatever the port speaks.
Type the whole address with http://, such as http://localhost:3000.
Highlight an old https:// suggestion in the address bar and press Shift+Delete (Shift+fn+Delete on Mac).
Change https:// links, redirects and asset URLs in your app that target this port.
- Check next
Find what forces https:// onto your port
Applies when http:// with a port reopens as https:// on that port. Without a port, Chrome tries 443 instead, which gives ERR_CONNECTION_REFUSED when nothing listens there.
Query the host under Query HSTS domain at chrome://net-internals/#hsts.
If static_upgrade_mode reads FORCE_HTTPS, serve that port over HTTPS with a certificate Chrome trusts.
If only dynamic_upgrade_mode reads FORCE_HTTPS, delete that saved rule there.
- Site owners
Make port 443 answer with TLS
Applies when every network gets this code on your site, and Google's help tells visitors to contact you. Something on port 443 replies in plain text or with an alert.
Trace port 443 through each hop: router, firewall, load balancer and server.
Point any hop that forwards 443 to a plain-HTTP port at your TLS listener.
If TLS starts but fails, read your server's TLS log for the alert it sent.
Stop the ERR_SSL_PROTOCOL_ERROR from coming back
Habits for developers and site owners that keep http:// services and HTTPS rules from colliding.
Pick .test for dev host names; RFC 6761 reserves it for testing, and Chrome preloads no .test rule.
Raise HSTS max-age in stages, starting at 300 seconds, so a forgotten HTTP subdomain breaks only briefly.
Move internal subdomains to HTTPS before adding includeSubDomains or preload; leaving the preload list takes months.
Related errors
Learn more
FAQ
A proxy that covers Incognito
Set Geonode as your system proxy, and Chrome's Incognito windows use it as well.


