Check supported TLS versions and ciphers

To find out which TLS versions a server accepts, run `openssl s_client` once per version with the matching flag — `-tls1_3`, `-tls1_2`, `-tls1_1`, `-tls1` — and see which handshakes complete. A successful connection prints a `Protocol` and `Cipher` line; a refused version fails the handshake instead. The result is misleading on OpenSSL 3 unless you know that its default security level refuses TLS 1.0 and 1.1 on the client side, so those versions appear disabled on the server when it is your own OpenSSL declining them — add `-cipher DEFAULT@SECLEVEL=0` to test them honestly. Cipher selection is split by version: `-cipher` controls TLS 1.2 and below, while TLS 1.3 suites are selected with `-ciphersuites`, and using the wrong flag silently changes nothing.

Updated August 19, 2026 Verified against OpenSSL 3.5.7

Reading what was negotiated #

Every successful handshake reports the version and suite that were actually agreed. That is the ground truth — everything else on this page is a way of constraining the negotiation to find the edges:

What did this connection settle on?
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| grep -E "^ *(Protocol|Cipher) *:"
Expected output
Protocol  : TLSv1.3
Cipher    : TLS_AES_256_GCM_SHA384

Probing each version #

Ask for one version at a time. A version the server accepts completes the handshake; one it has disabled fails:

Which versions does the server accept?
for v in tls1_3 tls1_2 tls1_1 tls1; do
printf '%-8s ' "$v"
openssl s_client -"$v" -connect example.com:443 -servername example.com </dev/null 2>&1 \
  | grep -qE "^ *Cipher *: *[A-Z0-9]" && echo "accepted" || echo "refused"
done
Expected output
tls1_3   accepted
tls1_2   accepted
tls1_1   refused
tls1     refused

To test those two honestly, lower your own client’s policy for the duration of the probe:

Test TLS 1.0 without your client vetoing it first
openssl s_client -tls1 -cipher 'DEFAULT@SECLEVEL=0' \
-connect example.com:443 -servername example.com </dev/null 2>&1 | head -20

A server still accepting TLS 1.0 in 2026 is a finding, not a configuration to preserve.

Cipher suites, and the flag that catches everyone #

TLS 1.3 replaced the suite namespace entirely, and OpenSSL kept them on separate switches:

Version Flag Example value
TLS 1.2 and below -cipher ECDHE-RSA-AES256-GCM-SHA384
TLS 1.3 -ciphersuites TLS_AES_128_GCM_SHA256

Passing a TLS 1.3 suite to -cipher is accepted and does nothing, which reads as the server ignoring your request. Start from what your own build offers:

TLS 1.3 suites this OpenSSL supports
openssl ciphers -s -tls1_3 -v
Modern TLS 1.2 suites, expanded
openssl ciphers -v 'ECDHE+AESGCM'

Then test whether the server will accept a specific suite:

Will the server take this one suite?
openssl s_client -tls1_2 -cipher 'ECDHE-RSA-AES128-GCM-SHA256' \
-connect example.com:443 -servername example.com </dev/null 2>&1 \
| grep -E "^ *Cipher *:"

no ciphers available or a handshake failure alert means the server declined that suite.

Enumerating everything the server accepts #

Walk your build’s list and record which ones survive. This is slow — one handshake per suite — so narrow the list before running it:

Which TLS 1.2 suites does the server accept?
for c in $(openssl ciphers -s -tls1_2 'ECDHE' | tr ':' ' '); do
openssl s_client -tls1_2 -cipher "$c" \
  -connect example.com:443 -servername example.com </dev/null 2>&1 \
  | grep -qE "^ *Cipher *: *$c" && echo "accepted  $c"
done

ALPN and OCSP stapling #

Two other properties of the same handshake, worth checking while you are here. HTTP/2 requires successful ALPN negotiation:

Does the server negotiate HTTP/2?
openssl s_client -alpn h2 -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| grep -i "ALPN"
Expected output
ALPN protocol: h2

And stapling, which saves every visitor an OCSP round trip:

Is an OCSP response stapled?
openssl s_client -status -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| grep -A3 "OCSP response"

no response sent by server means stapling is off or the server’s fetch from the CA is failing — see OCSP vs CRL for what that costs.

To fetch and decode the certificate itself rather than interrogate the connection, see get a certificate from a server. If the handshake never starts and OpenSSL reports a wrong version number, the port is probably not speaking TLS at all — that is a different problem.

Frequently asked questions

Why does TLS 1.0 fail even against a server I know still allows it?
OpenSSL 3 ships with security level 1, which refuses the signature and cipher combinations TLS 1.0 needs, so your client rejects the connection before the server has a say. Add -cipher DEFAULT@SECLEVEL=0 to test it. Treat a server that requires this as one to replace.
What is the difference between -cipher and -ciphersuites?
-cipher selects cipher suites for TLS 1.2 and below; -ciphersuites selects the separate TLS 1.3 suite list. They are independent, and passing a TLS 1.3 suite name to -cipher is accepted and has no effect — which looks like the server ignoring your request.
Does openssl ciphers tell me what the server supports?
No. It lists what your local OpenSSL build supports and would be willing to offer. Only a completed handshake tells you anything about the server.
How do I check whether a server staples OCSP?
Add -status to the s_client command. A stapling server returns an OCSP Response section; no response sent by server means stapling is off or failing.
The handshake fails with "wrong version number" rather than a protocol alert. What is different?
That is not a version problem at all — it means the response was not a TLS record, which usually means the port is serving plaintext. See wrong version number.

References