# TLS cipher suites explained: names, AEAD and safe choices

> How to read a TLS 1.2 cipher suite name, why TLS 1.3 has only five suites, which ones to disable, and nginx/Apache configs that follow Mozilla's guidance.

Source: https://ssl.sh/learn/cipher-suites/
Last updated: 2026-11-10

A cipher suite is the named set of algorithms a TLS connection uses. A TLS 1.2 name lists the key exchange, authentication, bulk cipher and hash; a TLS 1.3 name lists only the AEAD cipher and key-derivation hash, and there are five. As of November 2026, enable the TLS 1.3 defaults plus TLS 1.2 ECDHE suites with AES-GCM or ChaCha20-Poly1305.

## Anatomy of a TLS 1.2 cipher suite name

A TLS 1.2 suite name is a sentence with four slots, and reading it tells you exactly what the connection will do.

```text
TLS _ ECDHE _ RSA _ WITH _ AES_128_GCM _ SHA256
      |       |            |             |
      |       |            |             +-- hash for the PRF (and the HMAC in CBC suites)
      |       |            +---------------- bulk cipher, key size and mode
      |       +----------------------------- authentication: the certificate's key type
      +------------------------------------- key exchange
```

| Slot | Common values | What it decides |
|---|---|---|
| Key exchange | `ECDHE`, `DHE`, `RSA` | How the shared secret is created; only ephemeral (EC)DHE gives forward secrecy |
| Authentication | `RSA`, `ECDSA` | Which certificate key type can be used |
| Bulk cipher | `AES_128_GCM`, `AES_256_GCM`, `CHACHA20_POLY1305`, `AES_128_CBC`, `3DES_EDE_CBC` | How records are encrypted |
| Hash | `SHA256`, `SHA384`, `SHA` (SHA-1) | The TLS PRF; in CBC suites also the record MAC |

When the key exchange and authentication are the same algorithm, the name collapses: `TLS_RSA_WITH_AES_128_CBC_SHA` means RSA key transport *and* RSA authentication, with no ephemeral key at all.

OpenSSL uses its own shorter names. `openssl ciphers -stdname` prints both:

```bash
openssl ciphers -stdname 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA:AES128-SHA'
```

```text
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA    - ECDHE-RSA-AES128-SHA        TLSv1   Kx=ECDH Au=RSA Enc=AES(128)    Mac=SHA1
TLS_RSA_WITH_AES_128_CBC_SHA          - AES128-SHA                  SSLv3   Kx=RSA  Au=RSA Enc=AES(128)    Mac=SHA1
```

(The three TLS 1.3 lines OpenSSL always prints first are omitted here.) The `TLSv1` and `SSLv3` columns show the oldest protocol version a suite exists in, which is a quick hint that it is a legacy suite.

## TLS 1.3: five suites and nothing else

TLS 1.3 removed key exchange and authentication from the suite name. Key exchange is negotiated through `supported_groups` and `key_share`, signatures through `signature_algorithms`, and the cipher suite only picks the AEAD and the HKDF hash.

| Code | Name | Status in RFC 9846 (same as RFC 8446) | Enabled by default in OpenSSL 3.x |
|---|---|---|---|
| 0x1301 | `TLS_AES_128_GCM_SHA256` | Mandatory to implement | Yes |
| 0x1302 | `TLS_AES_256_GCM_SHA384` | Should implement | Yes |
| 0x1303 | `TLS_CHACHA20_POLY1305_SHA256` | Should implement | Yes |
| 0x1304 | `TLS_AES_128_CCM_SHA256` | Optional | No |
| 0x1305 | `TLS_AES_128_CCM_8_SHA256` | Optional, short 8-byte tag | No |

This change ended the combinatorial explosion of TLS 1.2, where the IANA registry accumulated hundreds of code points for every combination of exchange, signature, cipher and hash. It also means a TLS 1.3 suite name tells you nothing about whether the key exchange was X25519, P-256 or the hybrid X25519MLKEM768; tools report the group separately, for example `Negotiated TLS1.3 group: X25519MLKEM768` in `openssl s_client -brief`.

The July 2026 revision of TLS 1.3, RFC 9846, keeps the version number 0x0304 and the same five suites. Among its clarifications, it describes the key exchange in more generic language than the original RFC 8446, so that KEM-based key exchange such as ML-KEM fits the specification.

The two CCM suites exist for constrained devices. QUIC (RFC 9001) forbids `TLS_AES_128_CCM_8_SHA256` because its 8-byte tag is too short for general use.

## How a suite gets negotiated

The client proposes and the server decides. The ClientHello carries the client's full list of suites in preference order; the server picks one it also supports and names it in ServerHello. If there is no overlap, the server sends a `handshake_failure` alert.

In TLS 1.2 that single choice drags other constraints along:

- An `ECDHE_ECDSA` suite can only be picked if the server has an ECDSA certificate, and an `ECDHE_RSA` suite needs an RSA certificate.
- The ECDHE curve comes from the client's `supported_groups` extension, so a suite can be acceptable while no common curve exists.
- The signature over the ServerKeyExchange must use a hash the client listed in `signature_algorithms`.

In TLS 1.3 these three decisions are independent. The server picks an AEAD suite, a group from `key_share` (or asks for another one with HelloRetryRequest), and a signature scheme that matches its certificate, each from its own list. That is why adding a post-quantum group or an Ed25519 certificate does not require new cipher suites.

On the client side you often cannot edit the list directly. Browsers ship fixed lists that change with releases. Java reads `jdk.tls.disabledAlgorithms` from its `java.security` file, Windows Schannel takes its order from Group Policy or PowerShell (`Get-TlsCipherSuite`), and curl or Python inherit the defaults of the TLS library they are linked against. When an internal client fails against a hardened server, check which library and version it uses before touching the server.

## AEAD: why every modern suite uses it

AEAD (Authenticated Encryption with Associated Data) encrypts and authenticates in a single primitive, and TLS 1.3 allows nothing else. The three AEADs you will see are AES-GCM, ChaCha20-Poly1305 (RFC 7905) and AES-CCM.

Older TLS used CBC mode with a separate HMAC in a "MAC-then-encrypt" order. That construction leaked information through padding checks and timing, producing a decade of attacks: BEAST (2011) against TLS 1.0's predictable IVs, Lucky Thirteen (2013) timing attacks on CBC padding, and POODLE (2014) against SSL 3.0. AEAD modes have no padding oracle to attack. ChaCha20-Poly1305 is also fast in pure software, which is why phones and small ARM devices without AES instructions prefer it. See [AEAD](https://ssl.sh/glossary/aead/).

## Forward secrecy and the key exchange slot

Only suites whose key exchange starts with `ECDHE` or `DHE` provide [forward secrecy](https://ssl.sh/glossary/forward-secrecy/). With the static `RSA` key exchange, the client encrypts the pre-master secret to the server's certificate key, so anyone who later obtains that private key can decrypt every recorded session.

| Key exchange | Forward secrecy | Status |
|---|---|---|
| `RSA` (key transport) | No | Remove; also exposed to padding-oracle attacks such as ROBOT (2017) |
| `DHE` (finite field) | Yes | Slow and only safe with 2048-bit+ groups; dropped from Mozilla's 6.0 recommendations |
| `ECDHE` | Yes | Recommended for TLS 1.2 |
| TLS 1.3 (EC)DHE / hybrid | Yes | Always used, except pure-PSK resumption |

One caveat: session tickets are encrypted with a server-side ticket key. If that key lives for months and leaks, resumed sessions can be decrypted. Rotate ticket keys regularly or disable tickets on servers where you cannot.

## What to disable and why

Anything without AEAD, without forward secrecy, or with a broken primitive should be off. The table lists what you still find on legacy servers:

| Family | Example (OpenSSL name) | Problem |
|---|---|---|
| NULL | `NULL-SHA256` | No encryption at all |
| Anonymous | `ADH-AES128-SHA` | No authentication; trivial man-in-the-middle |
| Export | `EXP-RC4-MD5` | Deliberately weakened 40/56-bit keys; FREAK and Logjam (2015) |
| RC4 | `RC4-SHA` | Statistical biases; prohibited by RFC 7465 (2015) |
| DES / 3DES | `DES-CBC3-SHA` | 64-bit blocks; Sweet32 (2016) finds collisions after about 2^32 blocks (32 GB) |
| CBC with SHA-1 | `ECDHE-RSA-AES128-SHA` | MAC-then-encrypt padding and timing issues |
| Static RSA | `AES128-GCM-SHA256` | No forward secrecy |
| CCM_8 | `TLS_AES_128_CCM_8_SHA256` | Short tag; not for general web use |

Modern OpenSSL builds already exclude the worst of these. With OpenSSL 3.6, `openssl ciphers -v 'RC4'` and `openssl ciphers -v '3DES'` both return "no cipher match". RFC 9325 (BCP 195, November 2022) lists the suites the IETF currently recommends for TLS 1.2, which match the ECDHE plus AEAD list below.

## Recommended configuration

For a public server, follow Mozilla's intermediate profile: TLS 1.2 and 1.3, with six ECDHE suites for TLS 1.2. Mozilla's guidelines are the de facto reference, and version 6.0, current as of November 2026, defines two profiles:

- **Modern**: TLS 1.3 only, with `TLS_AES_128_GCM_SHA256`, `TLS_AES_256_GCM_SHA384` and `TLS_CHACHA20_POLY1305_SHA256`. Oldest compatible clients include Firefox 63, Chrome 70, Safari 12.1, Android 10 and Java 11.
- **Intermediate**: TLS 1.2 and 1.3. For TLS 1.2 it allows exactly six suites: `ECDHE-ECDSA-AES128-GCM-SHA256`, `ECDHE-RSA-AES128-GCM-SHA256`, `ECDHE-ECDSA-AES256-GCM-SHA384`, `ECDHE-RSA-AES256-GCM-SHA384`, `ECDHE-ECDSA-CHACHA20-POLY1305` and `ECDHE-RSA-CHACHA20-POLY1305`. Groups are `X25519MLKEM768`, `X25519`, `prime256v1` and `secp384r1`, and `server_preferred_order` is false.

Compared with version 5.7, 6.0 dropped the three DHE suites and added the post-quantum hybrid group. An nginx server block following the intermediate profile:

```nginx
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
# X25519MLKEM768 needs nginx linked against OpenSSL 3.5 or later
ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1:secp384r1;
```

`ssl_ciphers` only affects TLS 1.2 and below. TLS 1.3 suites come from OpenSSL's defaults, which are already the right three; to change them, nginx 1.19.4 and later accept `ssl_conf_command Ciphersuites ...`. If nginx is linked against an OpenSSL older than 3.5, drop `X25519MLKEM768` from the curve list or nginx will refuse to start.

The Apache equivalent uses the protocol prefix for TLS 1.3:

```apache
SSLProtocol             -all +TLSv1.2 +TLSv1.3
SSLCipherSuite          ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
SSLCipherSuite TLSv1.3  TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
SSLHonorCipherOrder     off
```

Notice that every TLS 1.2 suite appears twice, once for ECDSA and once for RSA certificates. A server can hold both certificate types and serve each client the one it supports. The [TLS config generator](https://ssl.sh/tools/tls-config-generator/) produces these snippets for other servers.

## Inspecting and testing suites with OpenSSL

`openssl ciphers -v` expands a cipher string into the exact suites it selects, so you can review a config before deploying it:

```bash
openssl ciphers -v 'ECDHE+AESGCM:ECDHE+CHACHA20' | grep TLSv1.2
```

```text
ECDHE-ECDSA-AES256-GCM-SHA384  TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256)            Mac=AEAD
ECDHE-RSA-AES256-GCM-SHA384    TLSv1.2 Kx=ECDH     Au=RSA   Enc=AESGCM(256)            Mac=AEAD
ECDHE-ECDSA-AES128-GCM-SHA256  TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(128)            Mac=AEAD
ECDHE-RSA-AES128-GCM-SHA256    TLSv1.2 Kx=ECDH     Au=RSA   Enc=AESGCM(128)            Mac=AEAD
ECDHE-ECDSA-CHACHA20-POLY1305  TLSv1.2 Kx=ECDH     Au=ECDSA Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-RSA-CHACHA20-POLY1305    TLSv1.2 Kx=ECDH     Au=RSA   Enc=CHACHA20/POLY1305(256) Mac=AEAD
```

To check what a live server accepts, force one suite at a time with `s_client`: `-cipher` for TLS 1.2 and below, `-ciphersuites` for TLS 1.3.

```bash
# Does the server still accept a CBC suite over TLS 1.2?
openssl s_client -connect example.com:443 -tls1_2 -cipher ECDHE-RSA-AES128-SHA </dev/null
# Does it accept ChaCha20 over TLS 1.3?
openssl s_client -connect example.com:443 -tls1_3 -ciphersuites TLS_CHACHA20_POLY1305_SHA256 </dev/null
```

A handshake failure alert means the suite is disabled. The [cipher and protocol cookbook](https://ssl.sh/cookbook/ciphers-and-protocols/) has a loop that tests every suite, and the [cipher suite lookup](https://ssl.sh/tools/cipher-suite-lookup/) decodes any IANA or OpenSSL name.

## Preference order and performance

With only strong suites enabled, order matters for performance rather than security. Clients list suites in their own preference; browsers on phones without AES acceleration put ChaCha20-Poly1305 first. If the server enforces its own order with AES-GCM first, those devices lose the faster cipher, which is why Mozilla now recommends letting the client choose. OpenSSL also has a `PrioritizeChaCha` option that keeps the server's order but honors a client that lists ChaCha20 first.

If a client and server share no suite at all, the handshake fails, and Chrome shows [ERR_SSL_VERSION_OR_CIPHER_MISMATCH](https://ssl.sh/errors/err-ssl-version-or-cipher-mismatch/). That usually means a server restricted to TLS 1.3 or exotic suites met an old client, or a legacy client only speaking 3DES or RC4 met a modern server. Fix the server list instead of re-enabling broken suites when you can.
