openssl s_client: Debug TLS Connections
openssl s_client connects to a TLS server and prints the handshake, certificate chain, and protocol details.
When TLS fails in a pipeline, s_client is the stethoscope. It shows the served chain, the negotiated version, and the verify result.
What it does
openssl s_client makes a TLS connection to host:port, performs the handshake, and prints the server certificate chain, negotiated protocol and cipher, and verification status. It then opens an interactive session unless you feed it input.
Common usage
echo | openssl s_client -connect example.com:443 -servername example.com
# show the full chain the server sends
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts
# force a protocol version
echo | openssl s_client -connect example.com:443 -tls1_2Options
| Flag | What it does |
|---|---|
| -connect host:port | Target server and port |
| -servername <name> | Send SNI (needed for name-based virtual hosts) |
| -showcerts | Print every certificate the server sends |
| -CAfile <file> | Trust anchors for verification |
| -tls1_2 / -tls1_3 | Force a TLS version |
| -starttls <proto> | Negotiate STARTTLS (smtp, imap, etc.) first |
In CI
Always pipe echo | (or use -quiet) so the command exits instead of waiting on stdin and hanging the job. Pass -servername explicitly; without SNI many hosts serve a default cert and you debug the wrong certificate.
Common errors in CI
"verify error:num=20:unable to get local issuer certificate" means the chain is incomplete or the CA is not in the runner trust store; add -CAfile or fix the server chain. "verify error:num=21:unable to verify the first certificate" is the same root cause one level down. "Connection refused" or "socket: Bad file descriptor" means the port is closed or unreachable.