
When a load balancer sits in front of your servers, SSL certificate problems become harder to diagnose. The certificate users see may not be the certificate on your origin servers. Renewals that work on one origin may not have been deployed to all of them. And the load balancer's own certificate may expire independently of your application's certificates.
Here's how to methodically diagnose certificate problems in a load-balanced environment.
Before diagnosing, understand which model you're using:
SSL termination at the load balancer (most common): The load balancer decrypts HTTPS traffic and forwards plain HTTP (or re-encrypts) to the origin servers. Users see the load balancer's certificate. The origin servers may have no TLS at all, or use internal/self-signed certificates.
SSL passthrough: The load balancer forwards encrypted traffic directly to origin servers without decrypting. The origin servers handle TLS themselves. Users see the origin's certificate directly.
End-to-end TLS (SSL bridging): The load balancer decrypts, inspects, and re-encrypts. Both the frontend (LB to client) and backend (LB to origin) certificates matter.
Which certificate the user sees depends entirely on which model is in use.
When users report a certificate error, first establish which server is serving the certificate they see.
# See which certificate your domain serves (from your perspective)
echo | openssl s_client -connect yourdomain.com:443 \
-servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
# Connect directly to a specific origin server (bypass load balancer)
# Replace ORIGIN_IP with the actual server IP
echo | openssl s_client -connect ORIGIN_IP:443 \
-servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -subject -dates
Compare the output. If the expiry dates or subjects differ, the load balancer and origin servers have different certificates — and depending on your SSL model, this may or may not be expected.
In SSL passthrough or end-to-end TLS setups, each origin server holds its own certificate. If you renew on server-1 but forget server-2, users routed to server-2 get an expired certificate — and the problem appears intermittently because the load balancer distributes traffic across both.
Symptoms: Users report SSL errors intermittently; the error doesn't reproduce consistently; the problem appears and disappears as traffic is routed to different origins.
How to check each origin directly:
#!/bin/bash
# Check certificate expiry on multiple origin servers
ORIGINS=("10.0.1.10" "10.0.1.11" "10.0.1.12")
DOMAIN="yourdomain.com"
for ip in "${ORIGINS[@]}"; do
expiry=$(echo | openssl s_client -connect "$ip:443" \
-servername "$DOMAIN" 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null)
echo "$ip: $expiry"
done
Any inconsistency in expiry dates points directly to the origin that wasn't updated.
If you're using SSL termination at the load balancer, the LB's certificate is the one users see — and it may be managed separately from your application's deployment pipeline. It's common for teams to have processes for renewing application certificates but forget the load balancer's certificate, which was set up once and never monitored.
Check the load balancer's certificate expiry:
# The certificate returned by this command is the LB's certificate
echo | openssl s_client -connect yourdomain.com:443 \
-servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -enddate
Most managed load balancers (AWS ALB, GCP Load Balancing, Azure Application Gateway) have their own certificate management within the cloud console — check there for expiry dates and set up renewal reminders independently of your server certificate process.
Server Name Indication (SNI) allows a server to serve different certificates for different hostnames on the same IP. If your load balancer isn't configured to pass the SNI hostname to origin servers, or if origin servers don't support SNI correctly, users may receive the wrong certificate.
Test SNI handling:
# Test with explicit SNI (what browsers do)
echo | openssl s_client -connect yourdomain.com:443 \
-servername yourdomain.com 2>/dev/null | grep "subject"
# Test without SNI (what happens if SNI fails)
echo | openssl s_client -connect yourdomain.com:443 2>/dev/null | grep "subject"
If the results differ, the server is returning different certificates based on SNI — this may be correct (multiple virtual hosts) or incorrect (fallback to a default certificate when SNI fails).
In a multi-origin setup, certificate deployment is often manual or scripted — and it's possible that the intermediate certificate bundle was deployed on some servers but not others. This produces the same intermittent pattern as an expired certificate on one origin.
# Check chain depth on each origin
for ip in "10.0.1.10" "10.0.1.11"; do
chain_depth=$(echo | openssl s_client -connect "$ip:443" \
-servername yourdomain.com 2>/dev/null \
| grep -c "BEGIN CERTIFICATE")
echo "$ip: $chain_depth certificate(s) in chain"
done
# You want 2+ on each origin
A count of 1 means the chain is incomplete on that origin. See incomplete certificate chain fix for the fix on each server type.
If your load balancer performs health checks over HTTPS, a certificate problem on an origin server may cause the LB to take that server out of rotation — without an obvious error message. Traffic silently concentrates on the remaining origins until they're also overwhelmed or fail.
Check your load balancer's health check configuration:
If the LB accepts invalid certificates in health checks, a certificate problem on an origin will go undetected at the LB level — users will still hit it.
After renewing certificates in a load-balanced environment, verify that the renewal was applied everywhere before considering the task complete:
#!/bin/bash
# Post-renewal verification checklist
DOMAIN="yourdomain.com"
ORIGINS=("10.0.1.10" "10.0.1.11")
echo "=== Load Balancer Certificate ==="
echo | openssl s_client -connect "$DOMAIN:443" -servername "$DOMAIN" 2>/dev/null \
| openssl x509 -noout -enddate -subject
echo ""
echo "=== Origin Certificates ==="
for ip in "${ORIGINS[@]}"; do
echo "--- $ip ---"
echo | openssl s_client -connect "$ip:443" -servername "$DOMAIN" 2>/dev/null \
| openssl x509 -noout -enddate -subject
done
All expiry dates should match (or the LB date should be independent of origins if you're using SSL termination with different certificate management).
The core challenge with load-balanced environments is that a problem on one origin is invisible from the outside until it causes user-facing errors. Domain Monitor monitors your SSL certificate expiry and HTTPS response from multiple external locations, helping catch inconsistencies. For multi-origin setups with direct access, monitoring each origin IP individually gives you the granular visibility to catch per-server certificate drift before it affects users. Create a free account.
Wildcard, SAN (multi-domain), and single-domain SSL certificates cover different use cases. Here's a clear comparison to help you pick the right type — and avoid paying for coverage you don't need.
Read moreDNS resolves correctly from your office but fails for users in other countries or on different ISPs. Here's why geographic DNS inconsistency happens and how to diagnose which layer is causing it.
Read moreRegistrar lock and transfer lock are often confused — and disabling the wrong one leaves your domain vulnerable. Here's a clear breakdown of what each does and when to use them.
Read moreLooking to monitor your website and domains? Join our platform and start today.