How to check TLS version of a website

Run curl -sI https://example.com/ -o /dev/null -v 2>&1 | grep 'SSL connection'. The line reads SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384, and the first field is the version this connection negotiated. Read the version from that field, never from the cipher name printed beside it.

Checker offline. Follow the manual steps below, they give the same answer.

Why check this

This check answers one question: which protocol version did the client and the server settle on right now. It runs after any change to a load balancer, a CDN setting or a reverse proxy, because a proxy in front of an updated origin can pin the connection to an older version than the origin supports. A payment integration that requires TLS 1.2 or higher fails the day a terminating proxy starts answering in TLS 1.1, and the application log shows a connection reset with no protocol detail.

Which version was negotiated is a different fact from which versions the server offers. This page covers the negotiated one. For the offered set, run the sweep in How to test SSL configuration.

Prerequisites

Steps

  1. Step 1.

    Ask curl to print the connection summary and keep the one line that names the version.

    curl -sI https://www.cloudflare.com/ -o /dev/null -v 2>&1 | grep 'SSL connection'
    
    * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / [blank] / UNDEF

    The first field after using is the protocol version. The second is the cipher suite. The two are separate facts.

  2. Step 2.

    Read the same value from a different TLS stack, so a single client's reporting quirk cannot mislead you.

    node -e "require('node:tls').connect({host:'www.cloudflare.com',port:443,servername:'www.cloudflare.com'},function(){console.log(this.getProtocol());this.end()})"
    
    TLSv1.3

    getProtocol() returns the negotiated protocol and nothing else, so there is no field to misread.

  3. Step 3.

    Force an old cipher suite on a modern server and compare what each tool prints. This is the trap the next section explains.

    openssl s_client -connect www.cloudflare.com:443 -servername www.cloudflare.com -tls1_2 -cipher 'AES128-SHA@SECLEVEL=0' </dev/null 2>&1 | grep '^New,'
    
    New, SSLv3, Cipher is AES128-SHA

    The connection is TLS 1.2. Node reports TLSv1.2 AES128-SHA SSLv3 for the same handshake, which shows that SSLv3 here is an attribute of the cipher.

How to read the result

| What you see | What it means | What to do | | --- | --- | --- | | SSL connection using TLSv1.3 | The handshake used TLS 1.3 | Nothing. Record it in the release notes. | | SSL connection using TLSv1.2 | TLS 1.2 was the highest both sides agreed on | Check whether the server offers 1.3 and the client refused it, or the reverse. | | SSL connection using TLSv1.0 or TLSv1.1 | A deprecated version is live | Treat as a release blocker. RFC 8996 deprecates both. | | New, SSLv3, Cipher is AES128-SHA | The cipher dates from SSL 3.0 | Ignore this field for versions. Get the version from step 1 or step 2. |

Common mistakes

Sign: openssl s_client prints New, SSLv3 and the report says the site still runs SSL 3.0.Cause: That line prints the version the cipher suite was introduced in, not the version the connection negotiated. AES128-SHA was defined for SSL 3.0 and is still legal in TLS 1.2, so a healthy TLS 1.2 handshake prints SSLv3 there.
Sign: The SSL-Session block with the Protocol line is missing from the openssl output.Cause: Under TLS 1.3 the session ticket arrives after the handshake. Feeding s_client from /dev/null closes the connection first, so the block is never printed. The version is still on the New line, with the caveat above.
Sign: Two testers on the same site report different versions.Cause: The negotiated version depends on the client. An old Java or Python runtime caps itself below what the server offers. Name the client and its TLS library next to every result.

What to check next

FAQ

How to check TLS version in Linux?

The same two commands. curl and OpenSSL ship on every mainstream distribution, and curl -v prints the SSL connection using line identically on Linux, macOS and Windows. No distribution specific tool is needed.

How to check TLS version with nmap?

nmap --script ssl-enum-ciphers lists every version a server offers. It answers the offered question, not the negotiated one, and it opens one connection per version and cipher. The commands above use one connection and need no extra install.

Does the browser padlock show the TLS version?

Chrome shows it under DevTools, Security tab, in the Connection section. That value is the browser's negotiated version, which can differ from curl's if the browser and curl disagree on supported versions.

Why does a server offer TLS 1.3 but negotiate 1.2?

The client did not offer 1.3. Old OpenSSL builds, Java 8 before update 261 and some corporate TLS proxies cap the client at 1.2, and the server takes the highest both sides list.

Verified

Verified by Maks Vernycurl 8.21.0openssl 3.1.1node 22.23.2

Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.

basic4 minpublished updated Maks Verny