Diagnose TLS Endpoints with openssl s_client
A browser says a site's TLS is broken but never says why, and openssl s_client is how you find out. It inspects the endpoint, checks the certificate name, shows the certificates sent, and can test a STARTTLS upgrade on a plain connection. About 10 minutes for a straightforward check. You need a shell, the OpenSSL package, and network access to the service under test.
The route
Jump straight to the step you need, or tick off Done means at the end.
These are diagnostic connections, not a replacement for the TLS settings in an application. The examples below use example.com; replace it with a host you are authorised to test.
1. Confirm the installed tool
Check the installed version and the options this guide relies on:
openssl version
openssl s_client -help 2>&1 | grep -E -- '-brief|-verify_hostname|-starttls|-showcerts|-verify_return_error'
On the machine used for this guide, the program reports OpenSSL 3.6.1, released on 27 January 2026. The installed manual is dated 18 August 2026 and identifies the command as OpenSSL 3.0.13 documentation, so check option details against the binary when moving between distributions.
2. Make a normal HTTPS handshake
Connect to the service with SNI and ask for a compact result:
openssl s_client \
-brief \
-connect example.com:443 \
-servername example.com </dev/null
-connect selects the destination. -servername puts the DNS name in the TLS Server Name Indication extension, essential when several sites share one address. OpenSSL 1.1.1 and later normally derive SNI from a DNS-style name in -connect, but writing it explicitly makes the test unambiguous and avoids surprises when you use an address, proxy or unusual target.
-brief keeps the result readable. A successful run includes lines like these, though the address, protocol, cipher and certificate change over time:
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: CN=example.com
Verification: OK
DONE
Skip the input redirection and the command stays interactive after the handshake: it displays received data and sends typed lines to the server. Use Q on a line by itself to exit, or keep </dev/null for a one-shot inspection.
3. Force certificate failures to fail the command
Run the handshake with an explicit hostname check and stop the moment verification fails:
openssl s_client \
-brief \
-connect example.com:443 \
-servername example.com \
-verify_hostname example.com \
-verify_return_error </dev/null
This distinction matters. s_client is a test tool and, by default, carries on after certificate verification errors so it can keep showing diagnostic information. A connection that prints a verification error is therefore not automatically a failed process, and it is not automatically an unsafe server either. -verify_return_error changes that and aborts the handshake when verification fails. -verify_hostname supplies the expected peer name for hostname verification.
Do not turn a passing s_client probe into a claim that every client is safe. Applications can use different trust stores, hostname rules, protocol limits or certificate policies. This command only tests the choices you give it.
4. Inspect the certificate chain and negotiated protocol
Request the server's sent certificate list when the compact output is not enough:
openssl s_client \
-connect example.com:443 \
-servername example.com \
-showcerts \
-prexit </dev/null
-showcerts prints the certificates the server sent, in the order it sent them. That is not a verified chain: check the verification result separately. -prexit prints session information when the program exits, including cases where a later request caused a problem, useful when a service only asks for a client certificate after a connection is established.
To test a protocol boundary, force a specific version and compare the result:
openssl s_client -brief -tls1_2 -connect example.com:443 -servername example.com </dev/null
openssl s_client -brief -tls1_3 -connect example.com:443 -servername example.com </dev/null
A failed forced-version test means that version was not negotiated with that endpoint under the local OpenSSL policy. It does not, by itself, tell you whether the server, a proxy or local policy caused the failure.
5. Test STARTTLS services
Tell s_client which clear-text protocol must upgrade before TLS:
openssl s_client \
-brief \
-starttls smtp \
-connect smtp.example.com:587 \
-servername smtp.example.com \
-name mail.example.com \
-verify_return_error </dev/null
-starttls supports protocol keywords including smtp, pop3, imap, ftp, xmpp, postgres, mysql, ldap and others listed by the local manual. For SMTP and LMTP, -name controls the name used in the EHLO or LHLO message: it is separate from TLS SNI. Use the name the service actually expects, not a made-up value.
Common traps and safe boundaries
- Wrong virtual host: connecting by IP can select a default certificate. Add
-servernamewith the intended DNS name. - False success: without
-verify_return_error, verification errors are displayed while the handshake may continue regardless. - Incomplete diagnosis:
-showcertsshows what was sent, not whether the chain is trusted. - Secret exposure: never use
-keylogfileon a shared system unless you deliberately need packet decryption. It writes TLS secrets that can decrypt captured traffic. - Client credentials:
-certonly matters when the server requests a client certificate. Keep private key files protected and never paste their contents into a terminal transcript. - Network impact: these commands make real connections and may send protocol greetings or HTTP requests. Do not probe systems without permission. No elevated privileges are needed for the examples.
Done means
- You confirmed which OpenSSL binary and version is installed.
- You recorded the negotiated protocol, cipher and peer certificate for the intended SNI name.
- You repeated the check with hostname verification and
-verify_return_error. - You used
-showcertswhen the server's sent chain needed inspection. - You selected the correct
-starttlsprotocol and separated its application name from TLS SNI.