# TLS 1.3: what changed from TLS 1.2 and how to enable it

> TLS 1.3 explained: removed legacy crypto, 1-RTT and 0-RTT handshakes, encrypted certificates, downgrade protection, compatibility and how to turn it on.

Source: https://ssl.sh/learn/tls-1-3/
Last updated: 2026-11-10

TLS 1.3 is the current version of Transport Layer Security, first published as RFC 8446 in August 2018 and now specified by RFC 9846 (July 2026). It needs one round trip instead of two for a new connection, encrypts most of the handshake and drops every legacy algorithm behind past attacks. Run it alongside hardened TLS 1.2 for older clients.

## What changed from TLS 1.2

TLS 1.3 is a redesign rather than an incremental update: it removes more than it adds.

| Area | TLS 1.2 (RFC 5246, now obsoleted by RFC 9846) | TLS 1.3 (RFC 9846, originally RFC 8446) |
|---|---|---|
| Full handshake | 2 round trips | 1 round trip |
| Resumption | Session IDs or tickets, 1 RTT | PSK tickets, optional 0-RTT data |
| Key exchange | Static RSA, DHE, ECDHE | Ephemeral (EC)DHE or hybrid only, or PSK with (EC)DHE |
| Forward secrecy | Only with (EC)DHE suites | Always for full handshakes |
| Ciphers | CBC, RC4, 3DES, GCM, ChaCha20... | AEAD only: AES-GCM, ChaCha20-Poly1305, AES-CCM |
| Cipher suites | Hundreds of combinations | Five |
| DH groups | Server can send arbitrary parameters | Named groups only (X25519, P-256, FFDHE from RFC 7919...) |
| Handshake signatures | RSA PKCS#1 v1.5, SHA-1 and MD5 allowed | RSA-PSS, ECDSA, EdDSA; PKCS#1 v1.5 only for legacy client keys (RFC 9963); no SHA-1 or MD5 |
| Certificate | Sent in clear | Encrypted |
| Compression | Supported (CRIME, 2012) | Removed |
| Renegotiation | Supported | Removed; replaced by KeyUpdate and post-handshake auth |
| ChangeCipherSpec | Real protocol message | Removed (dummy kept for middleboxes) |
| Key derivation | Custom PRF | HKDF with a structured key schedule |

The removed features were not decoration. Static RSA key exchange enabled Bleichenbacher-style attacks such as ROBOT (2017) and gave no forward secrecy. CBC padding enabled Lucky Thirteen; compression enabled CRIME; renegotiation needed an emergency fix in 2009; arbitrary DH parameters enabled Logjam. Each removal closed a class of bugs rather than one bug.

In July 2026 the IETF republished TLS 1.3 as RFC 9846. It is a minor, backward-compatible revision with the same version number: it clarifies text that was found unclear, forbids reusing key shares between connections, forbids negotiating TLS 1.0 and 1.1, limits the number of KeyUpdate messages, adds a `general_error` alert, and replaces the term "master" secret with "main". It also obsoletes RFC 5246, so the requirements for TLS 1.2 implementations now sit in the same document.

## Faster: 1-RTT and 0-RTT

TLS 1.3 saves a round trip by having the client guess. The ClientHello already contains key shares for the groups the client expects the server to accept, so the server can compute keys and send its certificate, signature and Finished in its first reply. The client can send an HTTP request right after its own Finished, one round trip after it started.

```text
TLS 1.2 full handshake                      TLS 1.3 full handshake
C: ClientHello              --->            C: ClientHello + key_share      --->
S: ServerHello, Certificate,                S: ServerHello + key_share,
   ServerKeyExchange,                          {EncryptedExtensions,
   ServerHelloDone          <---               Certificate, CertificateVerify,
C: ClientKeyExchange,                          Finished}                     <---
   ChangeCipherSpec, Finished --->          C: {Finished} + [GET /]         --->
S: ChangeCipherSpec, Finished <---          S: [200 OK]                     <---
C: [GET /]                  --->
S: [200 OK]                 <---
   = 2 RTT before the request                  = 1 RTT before the request
```

If the guess is wrong the server sends a HelloRetryRequest and the handshake costs two round trips, still no worse than TLS 1.2. Message-level details are in [the TLS 1.3 handshake](https://ssl.sh/learn/tls-handshake/).

Resumed connections can go further with 0-RTT: using a pre-shared key from an earlier session ticket, the client sends application data in its very first flight. The cost is security. Early data has no protection against replay, because the server cannot prove the data is fresh, and it is not forward secret with respect to the ticket key. The TLS 1.3 specification requires applications to opt in explicitly, and RFC 8470 defines the HTTP rules: the `Early-Data: 1` header and the `425 Too Early` status code. Most servers keep 0-RTT off by default; nginx's `ssl_early_data` directive defaults to `off`. See [0-RTT](https://ssl.sh/glossary/0-rtt/).

## More private: an encrypted handshake

In TLS 1.3 everything after ServerHello is encrypted, including the server's certificate chain. A passive observer of a TLS 1.2 connection could read the certificate, and therefore every hostname in its SAN list, straight from the wire; with TLS 1.3 it sees only ClientHello and ServerHello.

The remaining leak is the hostname in the [SNI](https://ssl.sh/glossary/sni/) extension of the ClientHello, which the server needs before it can pick a certificate. Encrypted Client Hello ([ECH](https://ssl.sh/glossary/ech/), standardized as RFC 9849 in March 2026) closes that gap: the client fetches the server's ECH public key from the HTTPS DNS record and encrypts the real ClientHello inside an outer one that names a shared front-end. ECH exists only for TLS 1.3, which is one more reason to enable it.

## Downgrade protection

An attacker who can make both sides believe the other only supports TLS 1.2 could steer them into weaker crypto. TLS 1.3 defends against this in two layers.

First, the real version is negotiated in the `supported_versions` extension, and the whole handshake is authenticated by the Finished messages and the server's signature. Second, when a TLS 1.3-capable server ends up negotiating an older version, it writes a sentinel into the last eight bytes of its ServerHello random:

```text
TLS 1.2 negotiated by a 1.3 server:   44 4F 57 4E 47 52 44 01   ("DOWNGRD" 01)
TLS 1.1 or lower:                      44 4F 57 4E 47 52 44 00   ("DOWNGRD" 00)
```

The server random is covered by the server's signature, so an attacker cannot remove the sentinel, and a TLS 1.3 client that sees it aborts the connection. This replaces the older `TLS_FALLBACK_SCSV` mechanism (RFC 7507), which only protected against browsers retrying with lower versions.

## Middlebox compatibility mode

A TLS 1.3 handshake is deliberately disguised as a TLS 1.2 session resumption. During standardization, field tests found that a noticeable share of firewalls, proxies and inspection boxes dropped connections that did not look like TLS 1.2, so the final design includes several pieces of camouflage:

- `ClientHello.legacy_version` is always `0x0303` (TLS 1.2); the real version list is in `supported_versions`.
- The record layer header also says `0x0303`, and encrypted records always use the outer type `application_data`.
- The client sends a random 32-byte `legacy_session_id`, which the server echoes.
- Both sides send a meaningless `ChangeCipherSpec` record.

That is why a packet capture of a TLS 1.3 connection shows "TLS 1.2" record headers and ChangeCipherSpec messages. In `openssl s_client -msg` output you will see lines such as `<<< TLS 1.2, RecordHeader` followed by `<<< TLS 1.3, ChangeCipherSpec`; that is expected.

## TLS 1.3 is where post-quantum key exchange lives

New key exchange methods are added to TLS 1.3 as named groups, without touching cipher suites. The most important is `X25519MLKEM768` (code point `0x11EC`), a hybrid of X25519 and the NIST-standardized ML-KEM-768, standardized in RFC 10024 (August 2026) together with SecP256r1MLKEM768 and SecP384r1MLKEM1024. Chrome enabled it by default in Chrome 131 in November 2024, and OpenSSL 3.5, released in April 2025, includes it in its default key shares. These hybrids are defined only for TLS 1.3, so a server still stuck on TLS 1.2 cannot offer quantum-resistant key exchange at all. Details in [post-quantum TLS](https://ssl.sh/learn/post-quantum-tls/).

## What stays the same

Your certificates, private keys and chains do not change when you enable TLS 1.3. The same RSA or ECDSA certificate serves TLS 1.2 and TLS 1.3 clients side by side, OCSP stapling and Signed Certificate Timestamps still work (they now travel as extensions on the Certificate entry), and HTTP-level settings such as [HSTS](https://ssl.sh/learn/hsts/) are unaffected. ALPN still selects `h2` or `http/1.1`, so HTTP/2 keeps working without changes.

One subtle difference concerns RSA keys. A TLS 1.3 server with an RSA key must sign the handshake with RSA-PSS, even when the certificate itself is signed with PKCS#1 v1.5, and the original RFC 8446 required RSA-PSS from clients too. Software keys handle this transparently, but some older hardware security modules, smart cards and TPM-backed client keys can only produce PKCS#1 v1.5 signatures. RFC 9963 (April 2026) added `rsa_pkcs1_*_legacy` code points that a server can offer in CertificateRequest for exactly these client keys. Where client or server does not implement RFC 9963, such keys still work over TLS 1.2 and fail over TLS 1.3, which typically shows up as client-certificate logins breaking after an upgrade. ECDSA keys are not affected.

Session resumption also changes shape. TLS 1.2 session IDs and tickets are replaced by TLS 1.3 PSK tickets, so shared session caches between servers behind a load balancer need their ticket keys synchronized as before, but old session-ID caches no longer help TLS 1.3 clients.

## Who supports TLS 1.3

Support is universal on current software; problems come from old installations.

| Software | First version with TLS 1.3 (final RFC) |
|---|---|
| OpenSSL | 1.1.1 (September 2018) |
| Chrome | 70 |
| Firefox | 63 |
| Safari | 12.1 |
| Android | 10 |
| Java | 11 (backported to 8u261) |
| Windows (Schannel) | Windows 11 and Windows Server 2022 |
| Apache httpd | 2.4.36, with OpenSSL 1.1.1 |
| nginx | 1.13.0, with OpenSSL 1.1.1 |

The weak spots are servers whose TLS library predates 2018. CentOS 7 and RHEL 7 ship OpenSSL 1.0.2, and Windows Server 2019 and earlier have no TLS 1.3 in Schannel. Software that bundles its own TLS library, such as Java or Go, is independent of the system OpenSSL.

## How to enable TLS 1.3

On a modern stack, enabling TLS 1.3 is a single word in the protocol list.

```nginx
# nginx (built with OpenSSL 1.1.1 or later)
ssl_protocols TLSv1.2 TLSv1.3;
```

```apache
# Apache httpd 2.4.36+ with OpenSSL 1.1.1+
SSLProtocol -all +TLSv1.2 +TLSv1.3
```

Check the library version first: `nginx -V 2>&1 | grep -i openssl` shows what nginx was built with, and `httpd -V` or `apachectl -M` helps for Apache. Then verify from outside:

```bash
openssl s_client -connect example.com:443 -servername example.com -tls1_3 -brief </dev/null
curl -sv --tlsv1.3 -o /dev/null https://example.com/ 2>&1 | grep -E 'SSL connection|TLSv1.3'
```

If the server cannot speak TLS 1.3, `s_client` fails with a protocol version alert. The [SSL checker](https://ssl.sh/tools/ssl-checker/) reports the supported versions, and the [cipher and protocol cookbook](https://ssl.sh/cookbook/ciphers-and-protocols/) has commands to test each version.

## Common deployment problems

Most TLS 1.3 issues come from layers around the web server rather than the protocol.

- **The edge is not your server.** If a CDN, cloud load balancer or WAF terminates TLS, its TLS policy decides the protocol. Your origin's nginx config does not matter to visitors.
- **TLS 1.3-only breaks old clients.** Mozilla's "modern" profile lists Firefox 63, Chrome 70, Safari 12.1, Android 10 and Java 11 as the oldest supported clients. Old Android phones, legacy Java apps and embedded devices fail with [ERR_SSL_VERSION_OR_CIPHER_MISMATCH](https://ssl.sh/errors/err-ssl-version-or-cipher-mismatch/) or a generic [protocol error](https://ssl.sh/errors/err-ssl-protocol-error/).
- **Client certificates per URL.** TLS 1.2 servers often requested a client certificate through renegotiation only for some paths. TLS 1.3 has no renegotiation, post-handshake authentication is not allowed with HTTP/2 (RFC 8740), and not every client implements it. Request client certificates for the whole host instead; see [mutual TLS](https://ssl.sh/learn/mtls/).
- **0-RTT replay.** Turning on early data without application checks can let replayed requests through.
- **TLS inspection boxes.** Old intercepting proxies may fail on TLS 1.3 or on the larger post-quantum ClientHello. Updating the box is the real fix.

TLS 1.0 and 1.1 should already be off: RFC 8996 deprecated both in March 2021, RFC 9846 now forbids negotiating them, and browsers removed them around 2020. A configuration of TLS 1.2 and 1.3 with the suites in [cipher suites](https://ssl.sh/learn/cipher-suites/) is the safe default as of November 2026.
