Updated April 2026

TLS Handshake Explained — How HTTPS Connections Are Established

Quick Answer

The TLS handshake authenticates the server and establishes a shared encryption key. TLS 1.3 does this in 1 round trip (1-RTT) vs TLS 1.2's 2 round trips (2-RTT). The handshake verifies the server certificate, negotiates a cipher suite, and derives session keys — all before any application data is sent.

Every HTTPS connection starts with a TLS handshake. It is invisible but adds latency and can fail in subtle ways — wrong certificate chain, unsupported cipher, expired cert. Understanding it helps you debug SSL errors and configure servers correctly.

Visualize your SSL chain →

TLS 1.3 handshake (modern — 1-RTT)

Client                          Server
  |                                |
  |--- ClientHello -------------->|  Supported ciphers, TLS version, key share
  |                                |
  |<-- ServerHello ______________|  Chosen cipher, key share
  |<-- Certificate _______________|  Server's cert chain
  |<-- CertificateVerify ________|  Proof server owns the private key
  |<-- Finished __________________|  Handshake MAC
  |                                |
  |--- Finished ----------------->|  Client confirms handshake
  |                                |
  |=== Encrypted application data ===|  HTTP request/response

TLS 1.3 completes in 1 round trip. Key exchange and cipher negotiation happen simultaneously in the first message.

TLS 1.2 handshake (legacy — 2-RTT)

Client                          Server
  |--- ClientHello -------------->|  Supported ciphers, random
  |<-- ServerHello ---------------|  Chosen cipher, random
  |<-- Certificate ---------------|  Server cert
  |<-- ServerHelloDone -----------|
  |                                |
  |--- ClientKeyExchange -------->|  Encrypted pre-master secret
  |--- ChangeCipherSpec --------->|
  |--- Finished ----------------->|
  |                                |
  |<-- ChangeCipherSpec -----------|
  |<-- Finished ------------------|
  |                                |
  |=== Encrypted application data ===|

Certificate verification — what the browser checks

1. Is the certificate for this domain?
   (Common Name or Subject Alternative Name matches)

2. Is the certificate expired?
   (Not Before <= today <= Not After)

3. Is the certificate signed by a trusted CA?
   (Chain: Leaf → Intermediate → Root in browser trust store)

4. Has the certificate been revoked?
   (OCSP check or CRL)

5. Does the server prove it owns the private key?
   (CertificateVerify signature in TLS 1.3)

What causes handshake failures

ErrorCauseFix
SSL_ERROR_BAD_CERT_DOMAINCertificate doesn't match domainReissue cert for correct domain
CERT_HAS_EXPIREDCertificate past Not After dateRenew certificate
UNABLE_TO_VERIFY_LEAF_SIGNATUREMissing intermediate certificateUse fullchain.pem in Nginx
SSL_ERROR_NO_CYPHER_OVERLAPNo common cipher suiteEnable TLS 1.2+ and ECDHE ciphers
HANDSHAKE_FAILURETLS version mismatchEnable TLS 1.2 minimum

Debug a TLS handshake

# Full handshake details
openssl s_client -connect example.com:443 -showcerts </dev/null 2>&1

# Check TLS version negotiated
openssl s_client -connect example.com:443 </dev/null 2>&1 | grep "New,"

# Test specific TLS version
openssl s_client -connect example.com:443 -tls1_2 </dev/null
openssl s_client -connect example.com:443 -tls1_3 </dev/null
Check your SSL chain and cipher suite →