Peer Failed to Perform TLS Handshake: Causes and Fixes
If you have run a `curl` request, a `git clone`, or a package update and been stopped cold by peer failed to perform TLS handshake, you have hit a connection that broke before any data could move. The error is blunt and unhelpful on its own, but it almost always points to one of a small set of root causes. This guide walks through what a TLS handshake is, why it fails, and how to diagnose and fix each cause methodically.
Key Takeaways
• A TLS handshake is the negotiation that happens before encrypted data flows; if the client and server cannot agree on protocol version, ciphers, or certificate trust, the handshake fails.
• The most common causes are a TLS version mismatch, an outdated or missing CA certificate bundle, an expired or incomplete server certificate chain, and a wrong system clock.
• Diagnose with `openssl s_client -connect host:443` to see exactly where the negotiation breaks.
• Surprisingly often the culprit is something local and trivial, not the server.
What Is a TLS Handshake and Why Does It Fail?
A TLS handshake is the opening conversation between a client (your browser, `curl`, `git`, or any HTTPS library) and a server before any encrypted application data is exchanged. During the handshake the two sides agree on a TLS protocol version (1.2, 1.3, and older deprecated versions), select a cipher suite, and the client verifies the server’s certificate against a trusted list of Certificate Authorities (CAs).
If any of these steps cannot be completed, the connection is aborted and tools such as gnutls (used by `git` and `curl` on many Linux distributions) report the now-familiar message:
“` gnutls_handshake() failed: Error in the pull function. * error: (-110) The TLS connection was non-properly terminated. * GnuTLS recv error (-110): The TLS connection was non-properly terminated. fatal: unable to access ‘https://example.com/repo.git/’: peer failed to perform TLS handshake “`
The phrasing differs between libraries. OpenSSL may say `SSL routines:ssl_choose_client_version:unsupported protocol`, while gnutls says `peer failed to perform TLS handshake`. The underlying problem is the same: the two endpoints could not reach agreement.
What Are the Common Causes and Fixes?
Before diving into individual fixes, the table below maps each symptom to its likely cause and the corresponding remedy. Use it as a quick triage reference.
| Symptom / Error detail | Likely cause | Fix |
|---|---|---|
| `unsupported protocol`, fails only on older servers | TLS version mismatch | Update client library or enable the required TLS version |
| `unable to get local issuer certificate` | Outdated or missing CA bundle | Update `ca-certificates`; point client at correct CA file |
| `certificate has expired` | Expired server certificate | Renew the server certificate; verify validity dates |
| `no cipher suites in common` | Cipher suite mismatch | Align cipher configuration on client or server |
| Works with IP, fails with hostname | SNI not sent or misconfigured | Use a client that sends SNI; fix virtual host config |
| `certificate is not yet valid` | System clock wrong | Sync the clock with NTP |
| Fails behind corporate network only | Firewall or proxy interception | Trust the proxy CA or bypass interception |
| `unable to get local issuer certificate` on a valid cert | Incomplete certificate chain | Install the full intermediate chain on the server |
Is It an Outdated TLS Version Mismatch?
Modern servers commonly require TLS 1.2 or TLS 1.3 and reject the deprecated TLS 1.0 and 1.1. If your client is old, it may only offer those deprecated versions, and the server refuses. The reverse also happens: a legacy server only speaks an old protocol that a hardened modern client declines.
Check what the server supports:
“`bash openssl s_client -connect example.com:443 -tls1_2 openssl s_client -connect example.com:443 -tls1_3 “`
If a specific version connects, you have found the mismatch. The right fix is almost always to update the client library (`curl`, `openssl`, `gnutls`) rather than to force a weak protocol. Forcing an outdated TLS version should be a temporary diagnostic step, not a permanent configuration.
Is Your CA Certificate Bundle Outdated or Missing?
Your client verifies the server certificate against a local bundle of trusted root CAs. If that bundle is stale, a perfectly valid server certificate signed by a newer or rotated root will be rejected with `unable to get local issuer certificate`.
On Debian and Ubuntu:
“`bash sudo apt update && sudo apt install –reinstall ca-certificates sudo update-ca-certificates “`
On RHEL, CentOS, and Fedora:
“`bash sudo yum reinstall ca-certificates sudo update-ca-trust “`
This single step resolves a large share of handshake failures, especially on long-running servers and containers built from old base images.
Is the Server Certificate Expired or Invalid?
A certificate past its expiry date, or one issued for a different hostname, will fail validation. Inspect the validity window directly:
“`bash echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates -subject “`
If `notAfter` is in the past, the server certificate must be renewed. If the `subject` or Subject Alternative Names do not include the hostname you requested, the certificate is misissued and must be replaced.
Could It Be a Cipher Suite Mismatch?
A handshake also fails when the client and server share no common cipher suite, often reported as `no cipher suites in common`. This appears when a hardened server disables older ciphers that an old client still relies on, or vice versa. List what the server offers:
“`bash nmap –script ssl-enum-ciphers -p 443 example.com “`
The durable fix is to update whichever side is stuck on legacy ciphers. Loosening a modern server’s cipher list to accommodate an outdated client weakens security and should be avoided.
Are SNI or System Clock Problems to Blame?
Server Name Indication (SNI) lets one IP serve many certificates by having the client announce the hostname during the handshake. A client that does not send SNI, or sends the wrong name, may receive the wrong certificate and fail. Always pass `-servername` when testing:
“`bash openssl s_client -connect example.com:443 -servername example.com “`
The system clock is the most underrated cause. Certificates are valid only between their `notBefore` and `notAfter` timestamps. If the machine’s clock is wrong, a valid certificate is read as `not yet valid` or already expired. This is common on freshly booted VMs, containers, and devices whose clock has drifted. Sync it:
“`bash sudo timedatectl set-ntp true timedatectl status “`
A surprising number of TLS handshake failures have nothing to do with the server at all. Before you escalate to the server owner or rewrite your TLS configuration, check two trivial things on the client: is the system clock correct, and is the CA bundle up to date? A clock that is off by days or a `ca-certificates` package that has not been updated since the image was built will reproduce the exact same `peer failed to perform TLS handshake` message that a genuine server misconfiguration would. These two checks take under a minute and resolve a disproportionate share of real-world cases. Rule them out first, and you will save yourself hours of chasing the wrong endpoint.
Is a Firewall, Proxy, or Incomplete Chain Interfering?
Corporate networks frequently run a TLS-intercepting proxy that re-signs traffic with its own CA. If your client does not trust that proxy CA, every handshake fails. The fix is to add the proxy’s root certificate to your trust store, or route around the interception for the affected endpoint.
An incomplete certificate chain is a server-side mistake: the server presents its leaf certificate but omits the intermediate certificates needed to link it to a trusted root. Browsers sometimes paper over this by caching intermediates, but `curl` and `git` do not. Confirm the chain depth:
“`bash openssl s_client -connect example.com:443 -servername example.com -showcerts “`
If only one certificate is shown where there should be a chain, the server administrator must install the full intermediate bundle.
How Do You Diagnose a TLS Handshake Failure Step by Step?
The single most useful diagnostic command is `openssl s_client`. It performs the handshake in isolation and prints exactly what was negotiated and where it broke:
“`bash openssl s_client -connect example.com:443 -servername example.com “`
Read the output methodically:
- Protocol and Cipher lines confirm a successful negotiation. Their absence points to a version or cipher mismatch.
- Verify return code: 0 (ok) means trust succeeded. Any other code names the validation failure, such as expiry or a missing issuer.
- Certificate chain section shows whether intermediates were sent.
For `curl` specifically, add `-v` to surface the library-level error, and for `git`, set `GIT_CURL_VERBOSE=1` to expose the underlying handshake messages.
Reliable SSL/TLS Hosting With DarazHost
Many handshake failures trace back to a server that was never configured correctly in the first place: an old TLS stack, a missing intermediate chain, or a certificate nobody renewed. DarazHost removes that class of problem at the source.
Every DarazHost plan ships with modern TLS 1.2 and TLS 1.3 enabled, complete certificate chains installed automatically, and AutoSSL to keep certificates issued and renewed without manual intervention. Free SSL is included across hosting plans, so your sites pass validation out of the box. For teams that manage their own stack, our VPS plans provide full root access, letting you tune cipher suites, protocol versions, and trust stores exactly how you need them. And when a TLS or SSL issue does surface, our 24/7 support team is available to diagnose handshake errors with you rather than leaving you to guess.
If you are tired of fighting certificate chains and protocol mismatches, gives you a platform where the handshake just works.
Frequently Asked Questions
What does “peer failed to perform TLS handshake” actually mean?
It means the client and server could not complete the encrypted-connection negotiation before any data was exchanged. They failed to agree on a TLS version, a cipher suite, or the client could not verify the server’s certificate against a trusted CA.
Why does the error appear in git but not in my browser?
Browsers ship and cache their own up-to-date CA bundles and intermediate certificates, and they support a wide range of TLS versions. Command-line tools such as `git`, `curl`, and `gnutls` rely on the system trust store, which is more easily outdated. An incomplete chain that a browser tolerates will often break `git`.
How do I fix it on Ubuntu or Debian quickly?
Start by updating the CA bundle with `sudo apt install –reinstall ca-certificates` followed by `sudo update-ca-certificates`, then confirm the system clock is correct with `timedatectl status`. Those two steps resolve a large share of cases without touching the server.
Can a wrong system clock really cause a TLS handshake failure?
Yes. Certificates are only valid within a fixed time window. If the machine’s clock is set before the certificate’s start date or after its expiry, validation fails and the handshake is aborted, even though the certificate is otherwise perfectly valid.
Is forcing an older TLS version a safe fix?
No. Forcing TLS 1.0 or 1.1 may make the connection succeed, but it exposes you to known protocol weaknesses. Use it only as a temporary diagnostic to confirm a version mismatch, then fix the real cause by updating the client or server.