DEV Community

Jeff W
Jeff W

Posted on

Your SSL Cert Works in Chrome. Here's Why It's Broken Everywhere Else.

"The site loads fine in my browser" is one of the least reassuring sentences in TLS debugging. Chrome (and every major browser) does a lot of quiet, generous error-correction that curl, older mobile OSes, most programming-language HTTP clients, and plenty of corporate proxies simply don't. A cert that looks perfectly fine with the padlock icon can still be actively broken for a meaningful slice of real traffic.

Here's what's actually going on, and how to check for it yourself instead of trusting the padlock.

The chain isn't just "your certificate"

A TLS handshake sends your leaf certificate (the one for your actual domain) plus zero or more intermediate certificates that chain it up to a root CA your client already trusts. Browsers ship with a huge cache of known intermediates and will silently fetch a missing one via the AIA (Authority Information Access) extension if your server doesn't send it. Most other clients won't. No caching, no AIA chasing; if your server doesn't hand over the full chain, the handshake just fails.

This is, by a wide margin, the single most common "works for me" TLS bug.

Check what your server is actually sending, not what your browser reconstructed for you:

openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -noout -issuer -subject
Enter fullscreen mode Exit fullscreen mode

Better, see the whole chain your server sent (count the s:/i: pairs — if there's only one certificate and it's not directly signed by a well-known root, you're missing intermediates):

openssl s_client -connect example.com:443 -servername example.com -showcerts < /dev/null 2>/dev/null
Enter fullscreen mode Exit fullscreen mode

Order matters too

Even when the intermediate is present, some servers send it in the wrong order (root before intermediate, or leaf last). The spec technically wants leaf first, then each subsequent issuer in order — plenty of servers get away with the wrong order because, again, browsers correct for it. openssl s_client will show you the order as sent; a client that validates strictly (which is more common outside browsers than you'd think) will reject an out-of-order chain even though every certificate in it is individually valid.

OCSP stapling: not required, but its absence has a cost

Without OCSP stapling, the client has to go ask the CA "is this cert still valid?" on its own, out-of-band, during the handshake. That's an extra network round-trip to a third party your client may or may not be able to reach quickly (or at all, behind certain corporate proxies/firewalls) — and some clients fail closed on a slow/failed OCSP check rather than just proceeding. Stapling means your server fetches that proof ahead of time and hands it over as part of the handshake itself — one less external dependency in the critical path of every single connection.

Check whether it's actually working (not just enabled in config):

openssl s_client -connect example.com:443 -servername example.com -status < /dev/null 2>/dev/null | grep -A1 "OCSP Response Status"
Enter fullscreen mode Exit fullscreen mode

successful (0x0) means it's really stapling a response. Nothing there at all is the far more common finding than you'd expect, even on sites that believe they've turned it on.

Cipher/protocol mismatches quietly cut off old clients

Disabling TLS 1.0/1.1 and weak ciphers is correct and you should do it, but "correct" and "zero impact" aren't the same thing. Older Android versions, some embedded/IoT HTTP clients, and legacy internal tooling can be stuck on a cipher/protocol combination your server no longer offers. This fails before certificate validation even happens, so it's easy to misdiagnose as a cert problem when it's actually a negotiation problem:

openssl s_client -connect example.com:443 -tls1_2 -cipher 'ECDHE-RSA-AES128-GCM-SHA256'
Enter fullscreen mode Exit fullscreen mode

If a specific old client is failing, negotiating with the closest matching protocol/cipher combination from your own machine tells you whether it's even in your server's supported set before you go looking anywhere else.

SNI: the handshake needs to know who it's talking to

If your server hosts multiple certificates behind one IP (extremely common now), it picks which one to present based on the SNI (Server Name Indication) value the client sends in its ClientHello. A client that doesn't send SNI at all — some older clients, certain bare-metal health checkers, a curl command that omits -servername when testing a non-default port — can get served the wrong certificate, or the server's default/fallback cert, and then fail validation for a hostname that's actually configured perfectly fine. Notice the -servername flag in every command above — that's not decorative, it's what makes the test represent a real browser request instead of a different, SNI-less one.


Checking all of this without memorizing openssl flags

The commands above are the ground truth, but running five of them by hand every time you touch a cert is exactly the kind of thing that gets skipped under time pressure — right up until a customer reports "it doesn't work" from some client you didn't test.

I built a free tool that runs the chain-completeness, order, OCSP-stapling, and TLS protocol/cipher checks above against any domain and shows you the results in one pass:

SSL Certificate Checker — full chain, key type/size, fingerprints, protocol support matrix, and OCSP status, for any public domain, no account needed.

(Disclosure: I built this — it's a free tool from ShieldIngress, a WAF/edge security product I run. No catch, no signup — I'd just rather people catch this before a client does.)

Top comments (0)