Peer failed to perform TLS handshake
Published 15 September 2026 · Updated 22 September 2026
Read the first word again. Peer means the other end: the message is reporting what the server did, not what your client got wrong. That single distinction decides where you go looking, and most advice about this error skips it entirely.
You will see this wording from GnuTLS-based clients: curl and
wget built against GnuTLS, aria2,
glib-networking, and from Postfix, FreeRADIUS and various Java and Go
services in their own phrasings. The underlying event is always the same:
the remote end sent a fatal TLS alert, or hung up, before the
handshake completed.
Seeing this on a TV, phone or laptop?
If the message appeared in a smart TV's built-in browser: LG TVs come up often, or in an app on your phone or computer, you do not need anything below this section. Work through these in order:
- Check the date and time. A clock that is wrong by more than a day makes every certificate look invalid. Set it to update automatically, then try again.
- Update the device. Sites keep dropping older encryption. A TV or phone that has missed years of updates may no longer speak the version the site insists on, and only a firmware or system update fixes that.
- Try another network. Switch the phone to mobile data, or leave hotel or office Wi-Fi. A sign-in page on public Wi-Fi, a filtering router or some antivirus products intercept secure connections, and the site refuses what they present.
- Try another device on the same network. If the site fails everywhere, the site itself is at fault and there is nothing to fix on your side. If it works elsewhere, the first device is out of date.
Restarting the TV or router is harmless, but it only helps when the real cause was one of the above.
First: is it you, or is it everyone?
Before changing anything, find out whether the server is refusing you or refusing everybody. One command settles it:
openssl s_client -connect example.com:443 -servername example.com
If OpenSSL completes the handshake and prints a certificate chain while your application fails, the server is fine and your client is the odd one out: go to causes 1, 2 or 3. If OpenSSL fails the same way, the problem is at the server or on the path, and causes 4, 5 and 6 are the candidates.
A second opinion from outside your network is worth having at this point, because it separates "the server is broken" from "something between me and the server is interfering". Our SSL checker connects from our infrastructure and reports what the server presents to it.
1. The client only offers TLS versions the server has dropped
The most common cause today, and it has been getting more common every
year. Servers have been disabling TLS 1.0 and 1.1 since roughly 2020. An
old client that offers nothing newer gets a
protocol_version alert and reports it as a handshake failure.
Test each version in turn: the first one that connects is your answer:
openssl s_client -connect example.com:443 -tls1_3 # TLS 1.3
openssl s_client -connect example.com:443 -tls1_2 # TLS 1.2
openssl s_client -connect example.com:443 -tls1_1 # should fail on a modern server
Fix it by updating the client, not by relaxing the server.
Re-enabling TLS 1.0 to placate one old script exposes every other user of
that server to a protocol with known, practical attacks. On Debian and
Ubuntu the usual culprit is an old libgnutls or a system
OpenSSL held back by the distribution release.
2. No cipher suite in common
Both ends agreed on a TLS version and still found nothing to talk with. You
will see a handshake_failure alert rather than a version
complaint.
This turns up when a hardened server has been cut back to a short list of modern suites, and the client is old enough to offer only what was removed. It also appears in the opposite direction: a FIPS-restricted client build that refuses everything the server offers.
nmap --script ssl-enum-ciphers -p 443 example.com
That lists what the server will accept. Compare it with what your client
offers: openssl ciphers -v, or gnutls-cli --list
for GnuTLS. If the intersection is empty, you have found it.
3. The client is not sending SNI
Server Name Indication travels in the ClientHello and tells the server which site you want. Without it, a host serving many certificates on one IP address has to guess. Some serve a default certificate, some abort the handshake outright.
This shows up when connecting by IP address rather than by
name, or from a very old client. The tell is that the handshake succeeds
with -servername and fails without it:
openssl s_client -connect 203.0.113.10:443 # fails
openssl s_client -connect 203.0.113.10:443 -servername example.com # works
If that is the difference, the fix belongs in the client: make it connect by hostname, or configure it to send SNI explicitly.
4. The server wants a client certificate
The most frequently missed cause. In mutual TLS the server asks the client to authenticate too. A client with no certificate configured sends an empty one, and the server responds with a fatal alert, which the client reports as a generic handshake failure, saying nothing about certificates at all.
openssl s_client makes this visible where most clients do not.
Look in the output for:
Acceptable client certificate CA names
If that section is present, the server is asking, and you need to supply a
certificate and key with -cert and -key. No
amount of protocol or cipher tuning will help here.
5. Certificate validation failed on the client
The server presented a certificate and your client rejected it, then closed the connection, so the failure is reported at handshake level even though the cause is trust.
The usual reasons are an incomplete chain, an expired certificate, a hostname that does not match, or a clock far enough out that a valid certificate reads as not-yet-valid. That last one is worth checking early on containers and freshly imaged machines, where the clock is often wrong by more than the certificate's validity window.
An incomplete chain is the most common of these and the most confusing, because browsers repair it silently and command-line tools do not. It has two opposite causes and the fix for one makes the other worse, which is covered in unable to get local issuer certificate.
6. It is not a TLS problem at all
Two impostors worth ruling out before you spend an afternoon on cipher suites.
Nothing is speaking TLS. If the port serves plaintext, or
a captive portal or proxy is intercepting, the bytes coming back are not a
ServerHello and the client gives up. openssl s_client printing
HTML, or a wrong version number error, is the giveaway.
The path cannot carry a large packet. This one is genuinely obscure and worth knowing. A ClientHello is small and gets through; the server's Certificate message is the first large packet in the exchange, and on a path with a broken MTU it vanishes silently. The symptom is a handshake that connects and then stalls, rather than one that fails immediately, and it will look identical to a server problem from your end. See SSH connects, then hangs, which is the same fault wearing a different costume.
What not to do
- Do not force TLS 1.0 or 1.1 to make one client work. It is a server-wide downgrade to fix a single caller, and it is the change you will be asked about in your next audit.
- Do not disable certificate verification:
curl -k,verify=False,InsecureSkipVerify. It converts a visible failure into an invisible one, and the connection it produces can be read by anyone on the path. - Do not assume it is your client. The word peer is telling you where to look, and roughly half the time it is right.
Check the server side in a few seconds. The Kodura SSL checker reads the chain exactly as the server sends it (no silent repairs) and reports the TLS versions accepted, the negotiated cipher, expiry and hostname coverage. That rules causes 1, 2 and 5 in or out without touching your client.
The order worth working in
openssl s_clientfrom the failing machine: does it also fail?- Check from outside, to separate the server from the path.
- Look for
Acceptable client certificate CA names. Free to check, frequently the answer. - Walk the TLS versions downward until one connects.
- Compare cipher lists only if the versions all fail.
Most of these end at step three or four. The cipher comparison in step five is where people usually start, which is why this error has a reputation for taking a whole afternoon.
If you saw the failure in a server log rather than from a client: nginx, Java, a Cloudflare 525, an AWS load balancer: SSL handshake failed works through what each of those sources means specifically, since each one narrows the cause differently.