How to check if TLS 1.0 is disabled
Run openssl s_client -connect example.com:443 -servername example.com -tls1 -cipher 'DEFAULT@SECLEVEL=0' </dev/null 2>&1 | grep error. A server with TLS 1.0 off answers tlsv1 alert protocol version and SSL alert number 70. Any other error means your own client refused before sending anything.
Why check this
A negative test passes for the wrong reason more often than any other check on this site. The command fails, the tester records "TLS 1.0 refused", and the failure came from the local OpenSSL security level rather than from the server. The audit item stays green while port 443 keeps answering TLS 1.0 handshakes. Run this on the release candidate and after every change to a TLS termination point, and run the control in step 2 every time so the result carries evidence that the client was capable.
Prerequisites
- OpenSSL 1.1.1 or 3.x. OpenSSL 3 sets security level 1 by default, which removes TLS 1.0 from the offer, so the
@SECLEVEL=0suffix is required to put it back. See SSL_CTX_set_security_level. - A control host that accepts TLS 1.0.
tls-v1-0.badssl.com:1010exists for this and is used below. - Shell access. A hosted scanner cannot show you the alert number, which is the part that distinguishes a real refusal.
Steps
- Step 1.
Offer TLS 1.0 to the server under test with the local security level lowered.
openssl s_client -connect www.cloudflare.com:443 -servername www.cloudflare.com -tls1 -cipher 'DEFAULT@SECLEVEL=0' </dev/null 2>&1 | grep error680F0000:error:0A00042E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version:../openssl-3.1.1/ssl/record/rec_layer_s3.c:1586:SSL alert number 70ssl3_read_bytesmeans the bytes came back from the network. The server sent the alert. - Step 2.
Run the identical command against a host known to accept TLS 1.0. This is the control that proves your client was able to make the offer.
openssl s_client -connect tls-v1-0.badssl.com:1010 -servername tls-v1-0.badssl.com -tls1 -cipher 'DEFAULT@SECLEVEL=0' </dev/null 2>&1 | grep '^New,'New, TLSv1.0, Cipher is ECDHE-RSA-AES256-SHAWithout this line, step 1 is not evidence of anything.
- Step 3.
Drop the cipher flag and watch the same command fail locally, so you can recognise the false pass.
openssl s_client -connect www.cloudflare.com:443 -servername www.cloudflare.com -tls1 </dev/null 2>&1 | grep error949D0000:error:0A0000BF:SSL routines:tls_setup_handshake:no protocols available:../openssl-3.1.1/ssl/statem/statem_lib.c:104:tls_setup_handshakeruns before the ClientHello goes out. Step 1 read 7 bytes back from the server, the alert record. This run reads 0. - Step 4.
Confirm that curl cannot answer this question on a modern build, so nobody repeats the test with it.
curl -I https://tls-v1-0.badssl.com:1010/ -o /dev/null --tlsv1.0 --tls-max 1.0curl: (35) TLS connect error: error:1404E0BF:SSL routines:ST_BEFORE_CONNECT:no protocols availableThe host in this command accepts TLS 1.0, and curl still reports a failure. The same message comes back from a server that refuses it.
How to read the result
| What you see | What it means | What to do |
| --- | --- | --- |
| alert protocol version with SSL alert number 70 | The server refused TLS 1.0 on the wire | Pass. Attach the alert line and the step 2 control to the ticket. |
| New, TLSv1.0, Cipher is ... | The server completed a TLS 1.0 handshake | Fail. TLS 1.0 is live on this listener. |
| no protocols available | Your OpenSSL refused before connecting | Not a result. Add -cipher 'DEFAULT@SECLEVEL=0' and run it again. |
| SSL alert number 40 | Version accepted, no shared cipher | The version may still be enabled. Widen the cipher list before concluding. |
| curl: (35) on any build | curl declined to speak TLS 1.0 | Ignore. Use OpenSSL for this check. |
Common mistakes
Thresholds
What to check next
- How to check TLS version of a website: the positive side, what a normal client negotiates here.
- How to check if a website supports TLS 1.3: the same forced-offer technique aimed at the top of the range.
- How to check cipher suites of a server: alert 40 sends you here, since it is a cipher answer rather than a version answer.
- How to test SSL configuration: runs all four versions in one loop.
FAQ
Does a PCI or ASV scan report replace this check?
It covers the hosts the scanner was pointed at. Internal listeners, staging load balancers and non-standard ports are usually outside its scope, and those are where an old TLS profile survives.
How do I test TLS 1.1 the same way?
Replace -tls1 with -tls1_1. The same @SECLEVEL=0 suffix is needed, and the same alert 70 is the expected refusal.
Can I run this from Windows without OpenSSL?
PowerShell can open a SslStream with an explicit protocol, which gives the same signal. Schannel builds of curl behave like the LibreSSL build in step 4 and cannot offer TLS 1.0 either.
Why does the server send alert 70 rather than closing the connection?
RFC 5246 defines protocol_version as the alert for a version the server will not accept. A silent close is also legal, so treat a connection reset with zero bytes read as a refusal that needs a second opinion.
Verified
Verified by Maks Vernyopenssl 3.1.1curl 8.21.0
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.
Related on this site
intermediate6 minpublished updated Maks Verny