SSL Debugging Checklist for Developers: 2026 Guide
By Nick Phillips, Founder
SSL Debugging Checklist for Developers: 2026 Guide

TL;DR:
- A systematic SSL debugging checklist helps developers quickly identify certificate, network, and application issues. Reproducing the TLS handshake from the failing environment and analyzing packet captures ensures accurate diagnosis before checking logs or relying on browser results. Automating SSL tests in CI catches certificate problems early and maintains secure, reliable connections.
A systematic SSL debugging checklist is the fastest way for developers to separate certificate problems from network faults and application errors. Most SSL connection issues trace back to a handful of root causes: expired certificates, incomplete cert chains, hostname mismatches, and environment drift between CI and production. Tools like openssl s_client, tcpdump, and Wireshark are the industry standard for reproducing and diagnosing TLS handshake failures at the transport layer. Working through a structured developer SSL checklist, rather than guessing from browser error messages, cuts debugging time significantly and prevents the same issues from recurring.
1. Reproduce the TLS handshake from the failing environment first
Reproducing TLS handshakes from the failing server is the single most important step in any SSL debugging guide. Running the test from your laptop while the failure happens on a backend service tells you almost nothing useful. The environment matters: firewall rules, DNS resolution, and trust stores all differ between machines.

Start with the basic openssl s_client command:
openssl s_client -connect example.com:443 -servername example.com
The -servername flag sends the correct SNI (Server Name Indication) header, which is critical for virtual hosts. Without correct SNI, the server may return a default certificate that does not match the hostname, producing a mismatch error that looks like a certificate problem but is actually a configuration problem.
Read the output top to bottom. The Certificate chain block shows every cert in the chain. The Verify return code line tells you whether the chain validates against the local trust store. A return code of 0 (ok) means the handshake succeeded. Anything else is your starting point.
Pro Tip: Use -tls1_2 or -tls1_3 flags to force a specific protocol version. This quickly reveals whether a failure is version-specific, which is common when a server drops TLS 1.0 and 1.1 support but a client has not caught up.
To inspect the full cert chain and pipe it for further analysis:
openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | openssl x509 -noout -dates
This prints the notBefore and notAfter dates directly. You can quickly eyeball whether the cert is expired or about to expire without opening a browser.
Common TLS errors like handshake failure and no shared ciphers usually mean incompatible protocol versions or cipher suites between client and server. Use the -cipher flag to test specific cipher suite compatibility when you suspect this is the issue.
2. Capture and analyze packets with tcpdump and Wireshark
Packet capture reveals what openssl s_client cannot: whether the handshake messages are actually reaching the server, and at which exact step the connection drops. Capturing targeted TLS traffic with tcpdump and analyzing it in Wireshark or tshark shows you the failure phase precisely.
Use a narrow capture filter to avoid noise:
tcpdump -i eth0 -w capture.pcap host example.com and port 443
Open the .pcap file in Wireshark and filter on ssl or tls. Look for these patterns:
- ClientHello with no ServerHello response: The server is not responding. Check firewall rules and routing between the two hosts.
- ServerHello followed by a fatal alert: The server rejected the handshake. The alert code tells you why (certificate unknown, handshake failure, etc.).
- Repeated ClientHello retransmissions: A network block is dropping packets before they reach the server. This is not an application problem.
- Certificate verify failure after ServerHello: The client received the cert but rejected it. Check the trust store and chain completeness.
- RST packet from server: The connection was actively refused, often by a load balancer or proxy, not the application itself.
Pro Tip: Combine packet capture with application logs. If your logs show a connection error at the same timestamp as a fatal TLS alert in the capture, you have triangulated the exact failure point. This saves hours compared to reading logs alone.
Keep capture windows short and targeted. Running a broad capture on a production host for minutes at a time creates operational risk and generates files too large to analyze quickly.
3. Verify certificate chain completeness and trust store currency
Skipping intermediate certificates or running an outdated CA bundle on clients causes the unable to get local issuer certificate error. This is one of the most common SSL certificate troubleshooting failures, and it almost always looks like a server problem when it is actually a client configuration problem.
Work through this checklist for every environment:
- Certificate expiry: Check
notAfterwithopenssl x509 -noout -dates. A cert that expired yesterday will fail silently in some clients and loudly in others. - Hostname matching: Verify the Common Name (CN) or Subject Alternative Name (SAN) fields match the hostname exactly. Wildcards like
*.example.comdo not coverexample.comitself. - Full chain delivery: The server must send the leaf cert plus all intermediate certs. Use
-showcertsinopenssl s_clientto count the chain depth. - CA bundle currency: Client trust stores drift over time. A CA that was valid two years ago may have been revoked or replaced. Update your CA bundle regularly, especially in containerized environments where base images age quietly.
- Supported TLS versions: Confirm the server supports TLS 1.2 at minimum. TLS 1.0 and 1.1 are deprecated. Many clients now reject them outright.
- Cipher suite overlap: If the client and server share no common cipher suites, the handshake fails immediately. Use
openssl ciphersto list what your client supports.
Diagnosing SSL errors systematically, starting with expiry and chain completeness, resolves the majority of cases before you need to go deeper into protocol analysis.
| Check | Tool | What to look for |
|---|---|---|
| Certificate expiry | openssl x509 -noout -dates |
notAfter date is in the future |
| Chain completeness | openssl s_client -showcerts |
All intermediates present in output |
| Hostname match | openssl x509 -noout -text |
CN or SAN matches request hostname |
| TLS version support | openssl s_client -tls1_2 |
Handshake completes without error |
| Trust store validity | openssl verify -CAfile bundle.pem |
Returns OK |
You can also run a quick check against any public domain using Otterwatch’s free SSL checker to get an instant read on chain status and expiry without touching the command line.
4. Do not trust the browser to tell you the truth
Browsers fetch missing intermediate certificates automatically and ignore certain trust store issues, which masks SSL misconfigurations that break backend clients and command-line tools. This is the single most misleading evidence in TLS debugging. If your browser says the connection is secure, that does not mean your backend service, mobile app, or API client will agree.
Always validate SSL connections from the actual client environment. A Node.js service, a Python requests call, and a Java HttpClient each use different trust stores and TLS stacks. What works in Chrome may fail in all three.
Test from the environment that is actually failing. If a microservice is throwing SSL errors, run openssl s_client from inside that container or VM, not from your development machine. The difference in trust stores alone can explain the discrepancy.
5. Integrate SSL smoke tests into your CI/CD pipeline
Automated handshake smoke tests in CI and production environments detect TLS policy drift before deployment failures reach users. A lightweight script running openssl s_client on every deploy takes seconds and catches certificate misconfigurations before they go live.
A minimal smoke test looks like this:
echo | openssl s_client -connect example.com:443 -servername example.com 2>&1 | grep "Verify return code"
If the output is not Verify return code: 0 (ok), the build fails. That is the whole test. Add it to your deployment pipeline and you catch the majority of cert chain and expiry issues automatically.
Practical strategies for multi-environment SSL testing:
- Test from CI, not localhost: Run handshake tests from your CI runner, which shares network characteristics with production.
- Pin TLS version expectations: If production requires TLS 1.2 or higher, assert that in the test. Do not let a TLS 1.0 fallback slip through.
- Monitor handshake success rates: Track handshake failures as a reliability metric in production. A spike in failures is an early warning of cert rotation problems or trust store drift.
- Check both internal and external endpoints: Internal services behind load balancers often have separate cert chains that get missed in external-only checks.
- Separate network faults from application faults: If the smoke test passes but the application still reports SSL errors, the problem is in the application’s TLS stack, not the certificate itself.
Pro Tip: Add an SSL monitoring step to your CI/CD pipeline that checks expiry dates 30 days out. A cert that expires in three weeks will not fail a handshake test today, but it will cause an outage next month.
Ensuring parity between CI, production, and developer nodes for network paths and trust stores is critical. Testing SSL from identical environments reveals breakage caused by firewall rules, DNS, or trust store differences, not code changes.
Key takeaways
A systematic SSL debugging checklist, starting with handshake reproduction and ending with automated CI monitoring, resolves the vast majority of TLS failures before they reach production users.
| Point | Details |
|---|---|
| Reproduce from the failing host | Run openssl s_client from the exact environment throwing errors, not your local machine. |
| Check chain and expiry first | Most SSL failures trace to an expired cert or missing intermediate before anything else. |
| Never trust browser success alone | Browsers mask misconfigurations that break backend clients and API consumers. |
| Capture packets for network faults | Use tcpdump and Wireshark to identify whether failures happen at the network or application layer. |
| Automate handshake tests in CI | Lightweight openssl smoke tests in your pipeline catch cert drift before deployments go live. |
Why I think most SSL debugging guides get the order wrong
Most SSL debugging guides tell you to start by reading error messages in your application logs. That is the wrong starting point. Application logs tell you what the TLS stack reported, not what actually happened on the wire. By the time you are reading a CERTIFICATE_VERIFY_FAILED message in your app, you have already lost the context you need to diagnose it quickly.
The order matters enormously. Reproduce the handshake first, from the right environment, with openssl s_client. Then check the cert chain. Then look at the logs. Working backwards from log messages into packet captures wastes time and leads to misdiagnosis.
The other thing I see trip developers up constantly is the browser test. Someone opens Chrome, sees the padlock, and closes the ticket. Three days later, a backend service starts throwing SSL errors because it uses a different trust store and the intermediate cert was never delivered by the server. Browsers are too forgiving to be useful as a debugging tool. They are great for end users. They are actively misleading for developers trying to diagnose TLS failures.
Documentation discipline matters too. Every time you work through an SSL issue, write down the exact commands you ran, the error codes you saw, and what fixed it. A personal SSL debugging runbook built from real incidents is worth more than any generic checklist. The patterns repeat. The commands that worked last time usually work again.
— Nick Phillips
Otterwatch keeps an eye on your certs so you do not have to
SSL debugging is reactive by nature. You run through the checklist because something already broke. The better position is knowing about a certificate problem before it causes a handshake failure at all.

Otterwatch watches your SSL certificates around the clock and sends you a plain, friendly heads up well before expiry. No dashboards to configure, no alert fatigue. Otis, the park ranger otter behind Otterwatch, keeps things calm and simple. You can check any SSL certificate for free right now, or sign up to have Otterwatch monitor up to five sites at no cost. When a cert is getting close to expiry or something looks off, you hear about it early, not when users start seeing browser warnings.
FAQ
What is the first step in debugging an SSL connection issue?
Reproduce the TLS handshake from the failing environment using openssl s_client -connect host:443 -servername host. This separates certificate problems from network and application faults immediately.
Why does SSL work in my browser but fail in my backend service?
Browsers automatically fetch missing intermediate certificates and tolerate certain trust store gaps that backend clients and command-line tools reject. Always test from the actual failing client environment.
How do I check if an SSL certificate chain is complete?
Run openssl s_client -connect host:443 -showcerts and count the certificates in the output. A complete chain includes the leaf certificate and all intermediate certificates up to the root CA.
What causes the “unable to get local issuer certificate” error?
This error means the server did not send intermediate certificates or the client trust store is outdated. Update the CA bundle and verify the server delivers the full chain.
How do I add SSL checks to a CI/CD pipeline?
Run a one-line openssl s_client command as a build step and assert the output contains Verify return code: 0 (ok). Fail the build on any other result to catch cert issues before deployment.
Recommended
- Signs Your SSL Certificate Is Failing: 8 Clear Warnings · Otterwatch
- SSL Expiry Notification Setup: A Practical Guide · Otterwatch
- How to check an SSL certificate’s expiration date (5 ways) · Otterwatch
- Common Certificate Deployment Errors: IT Troubleshooting Guide · Otterwatch
Catch the next cert expiry before your users do.
Otterwatch checks your SSL certificates daily and emails you 30 days before they expire. Five sites free.
Start watching →