toolhq.io

All posts
7 min readby Jameel Haider

TLS 1.2 vs TLS 1.3: what changed and what you should disable

TLS 1.3 was standardized in 2018 and is now the majority of traffic on the web. It is not an incremental revision of TLS 1.2: it deletes more than it adds. Everything that had been the source of a decade of protocol attacks, static RSA key exchange, CBC mode ciphers, renegotiation, compression, custom curves, was removed rather than patched. The result is a smaller protocol with a faster handshake and no bad options left to configure.

You can see which versions any host negotiates, and the cipher it chooses, with the SSL Checker.

What did TLS 1.3 remove?

TLS 1.2 supports roughly 37 cipher suite families, most of which are unsafe and disabled by careful operators. TLS 1.3 supports five, all of them authenticated encryption with associated data, and all of them safe. The whole category of "did I configure my ciphers correctly" mostly disappears with it.

The removals that matter:

  • Static RSA key exchange. In TLS 1.2 a server could encrypt the session key to its own RSA public key. Anyone who later obtained the private key could decrypt recorded traffic retroactively. TLS 1.3 mandates ephemeral key exchange, so forward secrecy is no longer optional. Recorded sessions stay unreadable even if the key leaks tomorrow.
  • CBC mode ciphers and RC4. The padding oracle family of attacks (BEAST, Lucky 13, POODLE against SSL 3) all targeted CBC construction. Only AEAD ciphers remain.
  • Compression and renegotiation. CRIME exploited the first; several attacks exploited the second. Both are gone, replaced by a narrower key update mechanism.
  • Weak curves and custom groups. The parameter negotiation surface that produced Logjam is gone.
  • SHA-1 signatures, following the same collision reality that ended SHA-1 in certificates.

The handshake: two round trips down to one

In TLS 1.2, the client says hello, the server replies with its certificate and parameters, the client responds with its key share, and only then can application data flow. That is two full round trips before the first byte of your request, on top of the TCP handshake.

TLS 1.3 makes an informed guess. The client sends its key share alongside the very first hello, guessing which group the server will pick; servers almost always accept, so the server replies with its own key share, certificate, and finished message all at once, and the client can send application data immediately. One round trip, and on a link with 80 ms latency that is 80 ms saved on every fresh connection. The certificate itself is also now encrypted, so a passive observer no longer learns which site you are visiting from the handshake alone, though SNI still leaks the hostname unless encrypted client hello is in use.

TLS 1.2TLS 1.3
Full handshake2 round trips1 round trip
Resumed handshake1 round trip0 round trips (optional)
Cipher suites~37 families, many unsafe5, all AEAD
Forward secrecyOptionalMandatory
Certificate in handshakePlaintextEncrypted
RenegotiationYesRemoved
Signature algorithmsIncludes SHA-1SHA-256 and above

What is 0-RTT, and should you enable it?

TLS 1.3 adds zero round trip resumption: a returning client can send application data in its very first packet, using a key derived from the previous session. The latency win is real and the tradeoff is specific.

0-RTT data has no replay protection. An attacker who captures that first encrypted flight can send it again, and the server will process it a second time. For a GET of a product page that is harmless. For a POST that charges a card or transfers money, it is not. If you enable 0-RTT, restrict it to idempotent requests: safe methods only, no state changing endpoints. Most CDNs implement this restriction for you, and it is worth verifying rather than assuming. The distinction between safe and unsafe methods is covered in GET vs POST vs PUT vs PATCH.

Which versions should you still accept?

The answer in 2026 is short: TLS 1.2 and TLS 1.3, nothing else.

SSL 2 and SSL 3 have been broken for years. TLS 1.0 and TLS 1.1 were formally deprecated in 2021, are refused by every current browser, and fail PCI DSS. Leaving them enabled buys compatibility with clients that cannot reach you anyway and adds downgrade surface.

TLS 1.2 stays on because a long tail of API clients, older Java runtimes, and embedded devices still need it. If your traffic is browsers only, and you can prove it from logs, TLS 1.3 alone is viable and simplifies everything. Otherwise keep 1.2 with a restricted cipher list: ECDHE key exchange, AES-GCM or ChaCha20-Poly1305, and nothing with CBC, RC4, 3DES, or static RSA.

In nginx that is:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;

TLS 1.3 ignores ssl_ciphers entirely, since its five suites are fixed, so that line only governs the 1.2 fallback. ssl_prefer_server_ciphers off is deliberate: modern clients pick better than a static server ordering, particularly on phones where ChaCha20 outperforms AES without hardware acceleration. The full configuration is covered in secure nginx SSL config explained, or you can generate one with the nginx config generator.

Verifying it

Version and cipher negotiation is one command away:

openssl s_client -connect example.com:443 -tls1_3 </dev/null | head -20

Swap -tls1_3 for -tls1_2, -tls1_1, and -tls1 to confirm the old versions are actually refused rather than merely deprioritised. For a full view including the certificate chain, expiry, and negotiated protocol across all your hosts at once, the SSL Checker and bulk SSL checker cover it without the command line. If a client suddenly cannot connect after you tighten versions, ERR_SSL_VERSION_OR_CIPHER_MISMATCH explained walks through the diagnosis.