Guides

9 min readUpdated

ERR_SSL_PROTOCOL_ERROR: why the secure connection fails and how to fix it

Quick answer

ERR_SSL_PROTOCOL_ERROR means Chrome started a TLS handshake and received something it could not accept, so it stopped before loading any page. Chrome uses the code as a catch-all for handshake failures that have no more specific error. The usual causes are a server or proxy answering in plain HTTP on port 443, a faulty TLS terminator, or security software or a middlebox interfering. One openssl s_client test shows which.

01What does ERR_SSL_PROTOCOL_ERROR mean?

Chrome shows the error under the heading "This site can't provide a secure connection", with the line "example.com sent an invalid response". The TLS handshake, which sets up encryption before any HTTP request is sent, failed in a way Chrome could not classify more precisely. No page, cookie or cached file is involved yet.

The code is a catch-all by design. In Chromium's source, the function that translates TLS library errors into browser errors returns ERR_SSL_PROTOCOL_ERROR for anything without a more specific case. No shared version or cipher gives ERR_SSL_VERSION_OR_CIPHER_MISMATCH, a rejected client certificate ERR_BAD_SSL_CLIENT_AUTH_CERT, and an unrecognised name ERR_SSL_UNRECOGNIZED_NAME_ALERT. What remains is mostly malformed or unexpected data, such as plain HTTP where TLS was expected.

02Who can fix it? Causes sorted by fixer

The browser always generates the error, but the cause sits with the site owner, the visitor, or a device in between. Knowing which saves hours of fixes on the wrong side.

CauseWho fixes itHow to recognise itTest
Port 443 answers in plain HTTP: a listener without TLS, or a port forward to port 80Site ownerFails for everyone; curl says wrong version number, Firefox usually SSL_ERROR_RX_RECORD_TOO_LONGopenssl s_client; curl -sI http://example.com:443/
A TLS terminator or load balancer that drops or corrupts handshakesSite owner or hostFails from every network, often intermittentlyRepeat s_client; read the terminator's logs
Antivirus HTTPS scanning, a corporate proxy or a VPNVisitor or their IT teamWorks on another network, or with the scanner pausedCompare networks and devices
A middlebox that mishandles a large ClientHelloWhoever runs the middleboxOnly current Chrome fails, often with resetsCompare with curl or an older client
Only TLS 1.0 or 1.1, or obsolete ciphersSite ownerChrome shows ERR_SSL_VERSION_OR_CIPHER_MISMATCH insteads_client with -tls1_2 and -tls1_3
No certificate or site configured for the name, at the server or CDN edgeSite ownerStarts after a DNS or proxy change; often a version-or-cipher or unrecognised-name error insteads_client with the exact -servername

03How to fix ERR_SSL_PROTOCOL_ERROR as a visitor

First find out whether the error follows you or the site: open the address on another device using mobile data. If it fails there too, only the site owner can fix it; send them the time and the exact error. If it works there, try these steps in order.

  • Pause the HTTPS or web scanning in your antivirus or internet security suite, then reload.
  • Turn off the VPN, proxy or filtering DNS service, then reload.
  • Open the page in a private window to rule out extensions.
  • Update Chrome, and the operating system on older devices.
  • Check the date and time; a wrong clock usually causes a certificate error instead, but it takes seconds to rule out.
  • On a company or school network, ask IT whether a proxy inspects HTTPS.

04How to test the TLS handshake with openssl s_client

openssl s_client performs the handshake on its own and prints what went wrong. Always pass -servername, which sends the hostname as SNI the way a browser does, and -verify_hostname, which checks the certificate against that name. Without SNI, a server hosting several sites may present the wrong certificate or reject the handshake.

With -brief, a working handshake prints CONNECTION ESTABLISHED, the protocol version, the cipher suite and Verification: OK. Verification failures stay warnings unless you add -verify_return_error. A server that refuses TLS 1.3 but accepts TLS 1.2 still works in current browsers.

The last command sends plain HTTP to the TLS port on purpose. Correctly configured servers answer 400 or close the connection (nginx adds "The plain HTTP request was sent to HTTPS port"). A 200 means port 443 serves plain HTTP; so does a redirect, unless the server deliberately redirects plain requests sent to its TLS port, which nginx can do with error_page 497.

Handshake tests (OpenSSL 3.0.22 and curl 7.88.1 on Debian 12, run against badssl.com test hosts; replace example.com)
# full handshake with SNI and a hostname check
openssl s_client -connect example.com:443 -servername example.com -verify_hostname example.com -brief </dev/null

# which TLS versions does the server accept?
openssl s_client -connect example.com:443 -servername example.com -tls1_3 -brief </dev/null
openssl s_client -connect example.com:443 -servername example.com -tls1_2 -brief </dev/null

# make certificate problems fatal instead of a warning
openssl s_client -connect example.com:443 -servername example.com -verify_hostname example.com -verify_return_error -brief </dev/null

# does port 443 answer in plain HTTP?
curl -sI http://example.com:443/

05What the openssl output means

These are real lines from the tests above, run against badssl.com's deliberately broken hosts and local test servers with OpenSSL 3.0.22 on Debian 12; other builds word them slightly differently.

openssl printsMeaningWhat Chrome showsFix
CONNECTION ESTABLISHED ... Verification: OKThe handshake works from hereNo TLS error; suspect the visitor's networkCompare networks
ssl3_get_record:wrong version numberPlain HTTP where TLS was expectedERR_SSL_PROTOCOL_ERROREnable TLS on that listener or fix the port mapping
tlsv1 unrecognized name ... SSL alert number 112No site configured for that nameERR_SSL_UNRECOGNIZED_NAME_ALERTAdd the hostname to the server or proxy
sslv3 alert handshake failure ... SSL alert number 40 (with -tls1_3)That version is not acceptedNothing, if TLS 1.2 worksEnable TLS 1.3 when convenient
legacy sigalg disallowed or unsupported (TLS 1.0-only host)Stuck on TLS 1.0 or 1.1ERR_SSL_VERSION_OR_CIPHER_MISMATCHEnable TLS 1.2 and 1.3
verify error:num=62:hostname mismatch, or num=10:certificate has expiredA certificate problemA NET::ERR_CERT privacy warningFix or renew the certificate

06Why plain HTTP on port 443 breaks the handshake

The most common cause is easy to show with arithmetic. A TLS client expects the server's first five bytes to be a record header: one byte of content type, two of version, two of length. A misconfigured server answering in plain HTTP starts its reply with HTTP/1.1 400, and the client reads those characters as the header.

The fix is to enable TLS on the listener behind port 443, or to point the port forward or load balancer at the TLS port instead of port 80.

  • Byte 1, H (0x48), is read as the content type; RFC 8446 defines content types 20 to 23 (0x14 to 0x17).
  • Bytes 2 and 3, TT (0x5454), are read as the version; a real record's version starts with 0x03, so OpenSSL reports wrong version number.
  • Bytes 4 and 5, P/ (0x502F), are read as the length: 20,527 bytes, above the 16,384-byte (2^14) maximum in RFC 8446 section 5.1, which is why Firefox typically reports SSL_ERROR_RX_RECORD_TOO_LONG.
  • Chrome has no specific code for either failure, so it shows ERR_SSL_PROTOCOL_ERROR.
An nginx listener that really speaks TLS on port 443 (checked with nginx -t on nginx 1.22.1; certificate paths are examples)
server {
    listen 443 ssl;            # without "ssl" nginx answers plain HTTP here
    listen [::]:443 ssl;
    server_name example.com www.example.com;
    ssl_certificate     /etc/ssl/example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
}

07SSL handshake failed between a CDN and the origin

Behind a CDN or reverse proxy there are two TLS connections: browser to edge, and edge to origin. Only a failure on the first produces ERR_SSL_PROTOCOL_ERROR. A failure on the second is seen by the edge, which answers with an error page of its own: a 502 Bad Gateway from nginx, or a 525 "SSL handshake failed" or 526 page from Cloudflare, both listed in the Cloudflare error codes reference.

Providers call the edge-to-origin setting an SSL or encryption mode. In Cloudflare's terms, Flexible connects to the origin over HTTP, Full over HTTPS without certificate validation, and Full (strict) over HTTPS with it. Each failing row was reproduced with nginx as the proxy.

Edge connects to the origin withThe origin servesResultWho shows what
HTTP to port 80HTTP that redirects to HTTPSEndless redirectsBrowser: ERR_TOO_MANY_REDIRECTS
HTTPS to port 443Plain HTTP on 443Handshake fails: wrong version numberEdge: 502, or 525 at Cloudflare
HTTP to port 443TLS on 443Origin answers 400Visitor sees that 400
HTTPS without SNISeveral sites, chosen by nameunrecognized name alert, or the wrong certificateEdge: 502 or 525
HTTPS with certificate checksA self-signed, expired or incomplete chaincertificate verify errorEdge: 502, or 526 at Cloudflare
HTTPS with SNI and checksA valid certificate for the nameWorks, encrypted end to endNothing
nginx as the proxy: error.log lines from the failing rows, and settings that avoid them (reproduced and checked on nginx 1.22.1)
# error.log lines from the failing combinations
SSL_do_handshake() failed (SSL: error:0A00010B:SSL routines::wrong version number) while SSL handshaking to upstream
SSL_do_handshake() failed (SSL: error:0A000458:SSL routines::tlsv1 unrecognized name:SSL alert number 112) while SSL handshaking to upstream
upstream SSL certificate verify error: (21:unable to verify the first certificate) while SSL handshaking to upstream

# proxy to an HTTPS origin with SNI and certificate checks
location / {
    proxy_pass https://203.0.113.10;
    proxy_set_header Host $host;
    proxy_ssl_server_name on;          # send SNI; nginx's default is off
    proxy_ssl_name example.com;        # the name on the origin's certificate
    proxy_ssl_verify on;               # also off by default
    proxy_ssl_verify_depth 2;
    proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
}

08How site owners fix ERR_SSL_PROTOCOL_ERROR

Work down the list from the top; the first items cover most cases.

  • Make sure every listener on port 443 has TLS enabled, and that port forwards, NAT rules and load balancers send port 443 to the TLS port, not to port 80.
  • Offer TLS 1.2 and TLS 1.3. RFC 8996 deprecated TLS 1.0 and 1.1 in March 2021, and since Chrome 98 the error for sites without TLS 1.2 cannot be bypassed.
  • Serve a certificate for every hostname, including www, and after moving behind a CDN confirm the edge certificate covers them all before switching DNS.
  • If failures are intermittent, read the TLS terminator's or load balancer's logs; a worker dying mid-handshake can look like random protocol errors.
  • If only recent Chrome versions fail, test servers and middleboxes with a large ClientHello and update them; tldr.fail lists affected products.
  • Under a TLS handshake flood, a terminator out of capacity drops handshakes, which visitors see as resets and timeouts.

09What a reverse proxy can and cannot fix

A TLS-terminating proxy takes over the browser-facing handshake. Visitors get the proxy's TLS versions, ciphers and certificates whatever the origin runs, which removes most owner-side causes of this error, and the proxy can encrypt and verify its own connection to the origin.

It cannot fix the visitor's side: antivirus that intercepts HTTPS, a corporate proxy or a broken middlebox still breaks the handshake before the proxy sees it. It cannot help while DNS points at the old server, and an origin TLS mismatch becomes an edge status page rather than going away. Configuring the origin leg, with SNI and a valid certificate, stays the owner's job.

Questions

Frequently asked questions

Test from another network to find out. If the site fails on mobile data and on other devices too, the website or its hosting is misconfigured and only the owner can fix it. If it fails only on your computer or network, the usual causes are security software that scans HTTPS, a VPN or proxy, or a company filter.

Almost never. The TLS handshake happens before the browser sends any cookie or uses any cached page, so neither can break it. That advice comes from other errors, such as redirect loops, where a stale cookie really can be the cause. Here, try another network, pause HTTPS scanning, and contact the site owner if it still fails.

Browsers use different TLS libraries and send different handshake options, so a server or middlebox that mishandles one browser's handshake can still accept another's. A recent example is the larger handshake produced by post-quantum key exchange, which some devices fail to read. Test with curl and openssl, and check firewalls and proxies on the path for updates.

ERR_SSL_VERSION_OR_CIPHER_MISMATCH is specific: browser and server share no TLS version or cipher suite, typically because the server offers only TLS 1.0 or 1.1, or a CDN has no certificate for the name yet. ERR_SSL_PROTOCOL_ERROR is the catch-all for handshake failures without a specific code, most often data that is not TLS at all.

The browser reached the CDN without trouble, but the CDN could not complete a TLS handshake with the origin server, so the visitor's side is not involved. Typical causes are an origin without TLS on port 443, an origin that needs the hostname sent as SNI, or a certificate the CDN will not trust. Test the origin with openssl s_client and the real hostname.

Sources

Sources and verification

openssl s_client and curl outputs captured with OpenSSL 3.0.22 and curl 7.88.1 on Debian 12, October 2026, against example.com, badssl.com test hosts and local nginx 1.22.1 test servers (plain HTTP on a TLS port, SNI rejection, and the three edge-to-origin failures). The listener snippet was checked with nginx -t, the proxy snippet with nginx -t and live requests.

  1. 01net_error_list.h: ERR_SSL_PROTOCOL_ERROR and related codes · Chromium
  2. 02openssl_ssl_util.cc: mapping TLS library errors to browser errors · Chromium
  3. 03error_page_strings.grdp: error page headings and messages · Chromium
  4. 04RFC 8446: TLS 1.3, section 5.1 Record Layer · RFC Editor
  5. 05RFC 8996: Deprecating TLS 1.0 and TLS 1.1 · RFC Editor
  6. 06TLS 1.0 and TLS 1.1 removal · Chrome Platform Status
  7. 07openssl-s_client manual (OpenSSL 3.0) · OpenSSL
  8. 08SSLerrs.h: NSS error strings used by Firefox · Mozilla
  9. 09Module ngx_http_proxy_module: proxy_ssl_server_name, proxy_ssl_verify · nginx.org
  10. 10Module ngx_http_ssl_module · nginx.org
  11. 11SSL/TLS encryption modes · Cloudflare docs
  12. 12ERR_SSL_VERSION_OR_CIPHER_MISMATCH · Cloudflare docs
  13. 13Error 525: SSL handshake failed · Cloudflare docs
  14. 14Error 526: invalid SSL certificate · Cloudflare docs
  15. 15badssl.com: test hosts for broken TLS configurations · badssl.com
  16. 16tldr.fail: servers that fail on a large TLS ClientHello · tldr.fail

Last updated