What the error actually says
The full Chrome message reads: “This site can’t provide a secure connection. example.com uses an unsupported protocol. ERR_SSL_VERSION_OR_CIPHER_MISMATCH. Unsupported protocol: The client and server don’t support a common SSL protocol version or cipher suite.”
That is a description of the very first step of HTTPS. The browser opens the connection with a ClientHello that lists the TLS versions and cipher suites it is willing to use. The server has to pick one version and one cipher suite from those lists that it also supports, and that work with the certificate it holds. If it can’t, it sends an alert and hangs up.
Two things follow from that:
- The certificate hasn’t been checked yet. This error happens before any certificate validation. Renewing or replacing a certificate won’t fix it, unless the real problem is that there is no certificate for that name at all (cause 3 below).
- Chrome uses this error name broadly. When a server answers with the generic
handshake_failurealert, Chrome shows it asERR_SSL_VERSION_OR_CIPHER_MISMATCHtoo, even if the server’s real reason was something else. So “version or cipher” is the most likely story, not a guaranteed one.
Firefox splits the same failures into SSL_ERROR_UNSUPPORTED_VERSION (no common TLS version) and SSL_ERROR_NO_CYPHER_OVERLAP (no common cipher).
If you’re just visiting the site
Try the same address on your phone over mobile data, not Wi-Fi.
- Fails there too: the site is misconfigured. Nothing on your device will fix it, and you shouldn’t try to lower your browser’s security for a public website. Let the owner know.
- Works there: something on your network or computer is in the way. A corporate firewall doing TLS inspection or antivirus “HTTPS scanning” can fail the handshake on the browser’s behalf (cause 4).
One legitimate exception: the admin page of an old router, NAS, printer or server management card on your own network. These often speak only TLS 1.0. Updating the firmware is the real fix. In the meantime, Firefox’s about:config setting security.tls.version.min (1 = TLS 1.0, 3 = TLS 1.2, the default) lets you get in. Put it back when you’re done.
If it’s your site: see what the server actually offers
Don’t read your config file and assume. Ask the server, using the exact hostname that fails, because the answer can differ per name.
Start with the two versions a current browser uses:
openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null
openssl s_client -connect example.com:443 -servername example.com -tls1_2 </dev/null
A working handshake prints the certificate chain and a line such as New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384. A failure ends with something like alert protocol version (alert number 70: the server doesn’t do this version) or alert handshake failure (alert number 40: no cipher in common, or the server refused for another reason), usually followed by no peer certificate available.
If both fail, find out whether the server only speaks older versions:
openssl s_client -connect example.com:443 -servername example.com -tls1 -cipher 'DEFAULT:@SECLEVEL=0' </dev/null
The @SECLEVEL=0 part is needed because OpenSSL 3.x won’t offer TLS 1.0 or 1.1 at its default security level. Some distributions compile them out entirely. If you get no protocols available, that’s your own OpenSSL refusing, not the server.
For a complete list of versions and ciphers the server accepts:
nmap --script ssl-enum-ciphers -p 443 example.com
It prints each protocol version the server accepts and the cipher suites under it. testssl.sh example.com gives an even more detailed report if you have it installed.
curl -v https://example.com/ is a quick cross-check: on success it prints a line starting with SSL connection using, followed by the version and cipher.
The SSL check does a handshake from outside your network and shows the negotiated version and the certificate returned for that name. If it connects and you can’t, the problem is on your side of the network, not on the server.
The usual causes
1. The server only speaks TLS 1.0 or 1.1
Chrome and Firefox turned off TLS 1.0 and 1.1 by default in 2020 (Chrome 84, Firefox 78), and RFC 8996 formally deprecated both in 2021. Chrome at first showed a warning you could click through; current versions simply refuse, and that refusal is this error. A server whose newest version is TLS 1.1 has no version in common with any current browser.
Where this still lives:
- Windows Server 2008 R2 and older IIS installs. TLS 1.2 exists there but is disabled for the server side unless someone turned it on in the registry.
- Software linked against OpenSSL older than 1.0.1, which has no TLS 1.2 at all.
- Old Java application servers, embedded web interfaces, and appliances that haven’t had a firmware update in years.
- A config line copied from an ancient tutorial, like
ssl_protocols TLSv1;.
Fix: enable TLS 1.2 and 1.3. If the software can’t do TLS 1.2, it needs an upgrade, or a reverse proxy in front of it (nginx, HAProxy, a CDN) that terminates TLS properly. On Windows, the protocol switches live under HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols, and a tool like IIS Crypto saves you from editing them by hand.
2. The cipher list has nothing a modern browser accepts
The server does TLS 1.2, but the cipher list only contains suites that browsers have removed:
- RC4, banned from TLS by RFC 7465 in 2015 and removed from all major browsers in early 2016.
- 3DES, DES, export-grade and NULL suites.
TLS_DHE_*(finite-field Diffie-Hellman) suites, which Chrome dropped in 2016.- Only
TLS_RSA_*suites (plain RSA key exchange, no ECDHE). Chrome has been removing these by default; a list made only of them is broken waiting to happen.
A subtler version: the cipher list only contains ECDSA suites (ECDHE-ECDSA-…) but the certificate is RSA, or the other way round. In TLS 1.2 the cipher suite determines which certificate type can be used, so the server has a cipher list and a certificate, but no suite that fits that certificate.
Before you deploy a cipher string, see what it actually expands to:
openssl ciphers -v 'ECDHE+AESGCM:ECDHE+CHACHA20'
If the output is empty or says Error in cipher list, your server will be offering nothing.
Fix: use a known-good list (see the configuration below) instead of hand-tuning. Mozilla’s SSL Configuration Generator, “intermediate” profile, is a sensible default for almost any public site.
3. No certificate for this name, especially on a CDN
This is the cause people miss, because it looks like a version problem and is really a certificate problem. Some edges, Cloudflare among them, abort the handshake when they have no certificate for the hostname the browser asked for (the SNI name). Chrome reports that abort as ERR_SSL_VERSION_OR_CIPHER_MISMATCH.
On Cloudflare, the typical situations are:
- A proxied name more than one level deep. The free Universal SSL certificate covers
example.comand*.example.com. A wildcard covers exactly one label, sodev.shop.example.comhas no certificate. You need an Advanced Certificate that covers*.shop.example.com(or Total TLS), or you set that record to DNS only and let your own server present its certificate. - A domain or hostname you just added. The edge certificate is still being issued. This usually takes minutes, occasionally longer. Check SSL/TLS → Edge Certificates; if the certificate shows as pending, wait or fix whatever blocks validation.
- Universal SSL was disabled and no other certificate covers the name.
You can confirm it with openssl against the same IP. If -servername example.com succeeds and -servername dev.shop.example.com fails with a handshake alert, the server works and the deep name simply has no certificate.
4. Something in the middle is breaking the handshake
If the server tests fine from outside but fails from one office or one computer, look between them. Corporate TLS-inspection firewalls and antivirus HTTPS scanning terminate your browser’s TLS and open their own connection to the site. When their TLS stack is old, they fail against a modern server, or they offer your browser something it won’t accept, and you get this error for a site that is perfectly healthy.
Fix: compare with the same URL on a network you control. If it’s a corporate device, the inspection product needs an update or an exception for that site; that’s a ticket for whoever runs the firewall.
5. The client is the old one
If the error comes from an app, a script or a device rather than current Chrome, turn the question around: maybe the server is modern and the client isn’t.
- Android before 5.0 doesn’t enable TLS 1.2 by default, and Android before 10 has no TLS 1.3. A TLS 1.3-only server locks out a lot of devices that are still in use.
- Windows’ built-in TLS stack, used by older .NET and PowerShell code, only has TLS 1.3 enabled from Windows 11 and Windows Server 2022.
- Hardened CDN settings do the same: a “minimum TLS version” of 1.3 at Cloudflare or a TLS 1.3-only load balancer policy rejects any client that stops at 1.2.
For a public site, TLS 1.2 plus 1.3 is the right floor. TLS 1.3 only is fine for an API whose clients you control.
A configuration that works
For nginx, a trimmed version of Mozilla’s intermediate profile:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
ssl_ciphers only controls TLS 1.2 and below. TLS 1.3 has its own small set of suites, all of them fine, and OpenSSL 1.1.1 or newer enables them for you.
For Apache:
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder off
Two traps when the change seems to have no effect:
- The setting lives in more than one place. Certbot’s nginx plugin, for example, includes
/etc/letsencrypt/options-ssl-nginx.confwith its ownssl_protocolsandssl_ciphers. Runnginx -T | grep -n 'ssl_protocols\|ssl_ciphers'to see every value nginx actually loaded. For Apache,apachectl -Sshows which virtual host answers on 443. - TLS isn’t terminated where you think. If a CDN or load balancer sits in front, browsers negotiate with it, not with your server. Change the edge’s minimum version or security policy there.
Reload, don’t just save: nginx -t && systemctl reload nginx, or apachectl configtest && systemctl reload apache2.
After the fix
- Re-run the
openssl s_client -tls1_2and-tls1_3tests for every hostname people use, not only the apex. - Run the SSL check to confirm an outside client now negotiates TLS 1.2 or 1.3 and receives the right certificate.
- If port 443 itself doesn’t answer from outside, that’s a different problem. The port check shows whether it’s reachable at all.
Related errors
ERR_SSL_PROTOCOL_ERROR: the handshake breaks in a less specific way, for example when the server answers plain HTTP on port 443.NET::ERR_CERT_COMMON_NAME_INVALID: the handshake succeeds, but the certificate doesn’t list the name you typed.NET::ERR_CERT_DATE_INVALID: the handshake succeeds, but the certificate has expired or isn’t valid yet, or the device clock is wrong.- Cloudflare error 525: the same kind of handshake failure, but between Cloudflare and your origin server instead of between the browser and Cloudflare.
Check your own domain now
Free, no sign-up. Runs the exact check this guide describes and shows what to fix.