A load balancer adds an extra hop between visitor and application — which also means an extra place for a handshake to fail, and troubleshooting requires figuring out which side of that hop the actual problem lives on before a fix makes sense.
Step 1: confirm where TLS is in practice terminating
Before troubleshooting further, confirm your specific architecture: does the load balancer terminate TLS and forward plain HTTP to the backend (the common pattern), or does it re-encrypt and forward HTTPS to the backend as well (end-to-end encryption)? This determines how many independent TLS handshakes are in fact involved, and therefore how many places a failure could originate from.
Testing against the load balancer
openssl s_client -connect load-balancer-ip:443 -servername yourdomain.com
This isolates whether the load balancer's own certificate and TLS configuration are correct, independent of anything happening further back at the application layer — if this fails, the problem is at the load balancer's own listener configuration, not the backend application.
If the load balancer's own handshake succeeds but requests still fail
The issue is likely further back — either in a re-encrypted load-balancer-to-backend hop (if that's your architecture), or in the backend application itself misinterpreting the connection it receives. A backend application expecting HTTPS but receiving plain HTTP forwarded by the load balancer (the common architecture) can produce confusing, application-level errors that look SSL-related but stem from a protocol mismatch at that specific internal hop.
Checking the forwarded-protocol header is being set and trusted correctly
If your application has its own logic that behaves differently based on whether the original connection was HTTPS (redirect logic, secure cookie flags, and similar), confirm it's correctly configured to trust the load balancer's X-Forwarded-Proto header rather than performing its own direct HTTPS check — a direct check will incorrectly read as false for every request behind a TLS-terminating load balancer, regardless of the actual original visitor connection's security.
Health check failures masquerading as handshake failures
If the load balancer's own health checks against backend targets are failing (due to a health check itself being misconfigured to expect HTTPS on a backend only serving plain HTTP, for example), the load balancer may mark targets as unhealthy and stop routing traffic to them entirely — producing a connection failure that looks handshake-related from the client's perspective, but is really a backend health check misconfiguration with no real TLS handshake problem at all. Check your load balancer's target health status directly before assuming the issue is certificate-related.
A systematic diagnostic sequence
- Test the load balancer's own certificate and TLS configuration immediately via its IP, isolated from DNS and from the backend entirely.
- Confirm backend target health status in the load balancer's own dashboard/console.
- If re-encrypting to the backend, test that internal hop's TLS configuration independently if you have network access to do so.
- Check application-level logs for the backend's own interpretation of incoming requests, particularly around protocol and forwarded headers.
What comparing a direct-to-origin test against a through-load-balancer test reveals
Testing the exact same request both outright against your origin server's IP and through the normal load-balanced path isolates whether a handshake failure originates at the load balancer layer or at the origin itself, a critical first diagnostic split before investigating either layer's configuration in detail.
How health check configuration can produce misleading handshake-related symptoms
A load balancer marking backend targets unhealthy due to a misconfigured health check (expecting HTTPS from a target only serving HTTP, for instance) can produce symptoms that look like a handshake failure to an end user, when the actual root cause is upstream in the health check configuration rather than the TLS handshake itself.
Why load balancer logs often contain more specific detail than the generic client-facing error
A client typically receives a generic, unhelpful connection error with no specific detail about what in reality failed — your load balancer's own access and error logs usually contain the specific, actionable detail (which backend target, what specific TLS error) needed to diagnose the issue, making them the right place to look first rather than relying solely on client-side symptoms.
What TLS version and cipher suite mismatches between load balancer and origin look like
If your load balancer's backend connection to the origin uses a different, more restrictive protocol or cipher configuration than the origin in practice supports, the backend hop itself can fail even though the load balancer's client-facing connection works perfectly fine — a distinct failure mode from a client-facing handshake issue, worth checking separately.
How to use packet capture tools when higher-level diagnostics aren't conclusive
When OpenSSL and curl-level diagnostics don't fully explain a handshake failure, a packet capture tool like tcpdump or Wireshark shows the exact raw network exchange, revealing details (a connection reset at a specific point, an unexpected packet) that higher-level tools abstract away — a deeper, more time-consuming but occasionally necessary level of diagnosis for in fact puzzling failures.
Why documenting your specific load balancer's TLS architecture speeds up future diagnosis
A clear written record of exactly where TLS terminates, what backend protocol is used, and what health check configuration exists saves considerable time for whoever troubleshoots the next handshake-related issue, rather than needing to rediscover the architecture from scratch during a stressful, time-sensitive incident.
What a staged elimination approach looks like for a truly complex multi-layer setup
For infrastructure with several layers (CDN, load balancer, reverse proxy, origin), testing each layer's boundary individually, working from the outermost layer inward, systematically eliminates layers one at a time until the specific layer causing the handshake failure is isolated, rather than trying to reason about the entire chain simultaneously.
How connection draining during a deployment can produce transient, hard-to-reproduce failures
A load balancer removing a backend target for a deployment, without properly draining in-flight connections first, can cause requests mid-handshake to fail abruptly — this produces a transient failure pattern correlated with deployment timing specifically, which can be mistaken for a persistent configuration issue if the deployment-timing correlation isn't noticed.
Why establishing a baseline of normal handshake timing helps identify when something's wrong
Knowing what normal handshake completion time looks like for your specific infrastructure under typical conditions gives you a concrete baseline to compare against when investigating a suspected issue — a handshake taking meaningfully longer than your established baseline is a concrete, measurable signal worth investigating even before a connection outright fails.
A quick checklist for diagnosing most load-balancer-related handshake failures
Test against the origin bypassing the load balancer, check health check configuration for a protocol mismatch, review load balancer access and error logs for specific detail, and confirm backend TLS configuration matches what the load balancer expects — four checks resolving most load-balancer-related handshake issues.
Why this category of issue rewards careful, layer-by-layer diagnosis over guessing
Multi-layer infrastructure (CDN, load balancer, origin) means a handshake failure could really originate at any one of several distinct layers — methodically testing each layer's boundary individually, rather than guessing based on which layer seems most likely, consistently converges on the actual cause faster than intuition alone.
Loading comments…