Post-quantum key exchange is already running on ordinary websites, including this one. The question is no longer whether it works, but what it costs. For the first packet your browser sends, the price is 1,178 extra bytes.

We measured that on our own connection rather than reading it off a specification, because the specification tells you how big a key is and not how big a handshake gets.

Two handshakes, same server

A TLS 1.3 client opens by sending a ClientHello containing, among other things, a key share — its half of the key agreement. The server answers with a ServerHello carrying its half. Everything after that is encrypted.

We asked recatools.com for a connection twice: once insisting on classical X25519, once on the hybrid X25519MLKEM768 that the site negotiates by default. Same host, same client, minutes apart.

Classical X25519Hybrid X25519MLKEM768Difference
ClientHello232 bytes1,410 bytes+1,178
ServerHello122 bytes1,210 bytes+1,088

The ClientHello grows 6.08 times. Not 6% — six times over.

A comparison of two live TLS 1.3 handshakes to recatools.com measured minutes apart. The classical X25519 handshake sends a ClientHello of 232 bytes and receives a ServerHello of 122 bytes. The hybrid X25519MLKEM768 handshake sends a ClientHello of 1,410 bytes and receives a ServerHello of 1,210 bytes. The ClientHello is therefore 1,178 bytes larger, a growth of 6.08 times, and the ServerHello is 1,088 bytes larger. A note records that the 1,088-byte server-side growth matches the ML-KEM-768 ciphertext size published in FIPS 203 exactly, and that the client-side growth of 1,178 bytes is close to the 1,184-byte ML-KEM-768 encapsulation key.
The server-side growth is not approximately ML-KEM's ciphertext. It is exactly it.

The numbers line up with the specification, which is the point

An X25519 public key is 32 bytes. An ML-KEM-768 encapsulation key is 1,184. The hybrid sends both, so the ClientHello should grow by about 1,184 bytes — we measured 1,178, the small shortfall being how the two shares are packed and framed rather than sent as separate extensions.

The server side is cleaner. ML-KEM-768's ciphertext is 1,088 bytes in FIPS 203, and the ServerHello grew by 1,088 bytes exactly.

That exactness matters. It is the difference between quoting a table and measuring what is happening on the wire. The specification predicts a number; the wire produced it. Had they disagreed, the interesting story would have been the disagreement.

Why the first packet is the one that matters

A 1,178-byte increase is trivial for a page of images, but not for a ClientHello. The cost shows up in two ways.

First, it is the first thing sent, before any connection is established, at the moment a user is waiting on a blank screen. Bytes there are on the critical path in a way that bytes in the third image are not.

Second, a classical ClientHello at 232 bytes fits inside a single packet with room to spare. At 1,410 bytes it is pressing against the ~1,500-byte limit of a typical Ethernet path, and on any path with a smaller limit — some mobile networks, some tunnels — it stops fitting and has to be split. A handshake that spans two packets is a handshake that can be delayed by the loss of either.

This is the practical shape of the post-quantum migration, and it is not what most coverage leads with. The cryptography is settled and standardised. The deployment problem is that the new keys do not fit where the old ones did.

We did not choose this, and that is the interesting part

Nobody here decided to turn on post-quantum key exchange. It arrived because our CDN made it the default, the same way encrypted client hello did. Our earlier guide on the migration that already started made the same point about presence; this one measures the bill.

This generalises. If your site sits behind a major CDN, this has probably already happened to you too, and the extra kilobyte is already going out on every fresh connection whether or not anyone on your team has read a FIPS document.

The half nobody has paid for yet

Key exchange is the part that has shipped. Signatures are the part that has not, and they are the larger bill.

Every handshake also carries a certificate chain, signed with whatever algorithm the certificate authority used. Those signatures are still classical — ECDSA or RSA — because the post-quantum replacements are considerably bigger than their key-exchange counterparts, and a certificate chain is sent on every connection just as a key share is. Nothing in the measurement above touches that, because our certificate chain has not changed.

The consequence is that the growth we measured is the cheap half arriving first. When signature algorithms follow, the same first-packet pressure applies again to a larger payload, and the chain cannot simply be trimmed — it is what proves the server is who it claims to be.

This is why "post-quantum is deployed" is a half-true sentence today. The key agreement is protected against a future attacker recording traffic now and decrypting it later. Authentication, which matters at the moment of connection rather than years afterwards, is still classical — and is a much less urgent problem for exactly that reason. An attacker with a quantum computer in 2040 cannot retroactively impersonate a server in 2026.

If you want to see what a certificate is actually asserting, our JWT decoder makes the same point on a smaller object: signed tokens are readable by anyone, and the signature proves origin rather than conferring secrecy. Our guide on why encoding is not encryption is the longer version of that distinction.

What this measurement is not

These are handshake bytes on one connection, from one client, to one host, on one day. A different edge server, a different TLS library, or a middlebox that intercepts and re-originates the connection would all move the numbers. Re-run the script rather than trusting the table if the answer matters to you.

It also says nothing about speed. Bigger handshakes are not automatically slower — the extra bytes cost transmission time and, on a constrained path, a risk of fragmentation, but ML-KEM's own arithmetic is fast and on many links the difference will not be visible. We measured size because size is what we can measure honestly from here; latency would need a controlled path and a great deal more care.

And it is not a comparison of signature schemes. ML-DSA and SLH-DSA change certificate sizes, which is the other half of the deployment story and a larger one — a certificate chain is sent on every handshake too. That belongs in its own measurement, not in a paragraph here.

A kilobyte, on the first packet

The migration is not a future project; it is already costing this site 1,178 bytes on the ClientHello and 1,088 on the ServerHello, on every fresh connection. That takes the first packet from 232 bytes to 1,410, close enough to a typical path limit that it may no longer travel alone. The server-side growth is exactly ML-KEM-768's published ciphertext, which is how you know the measurement is real rather than quoted. And none of it was decided here — it arrived with the CDN, and has probably arrived at your site too.

Sources and method
  • Both handshakes are performed by measure-pqc-bytes.cjs, committed with this guide, using openssl s_client -msg against recatools.com and summing the Handshake record lengths it reports. The script re-runs on demand; it does not read a stored result.
  • Component sizes are sourced, not measured: X25519's 32-byte public key from RFC 7748, and ML-KEM-768's 1,184-byte encapsulation key and 1,088-byte ciphertext from NIST FIPS 203. They are quoted to account for the measured difference, never in place of it.
  • Controls: the hybrid run must actually negotiate X25519MLKEM768 or the comparison is abandoned, and the hybrid ClientHello must exceed the classical one or the script stops rather than report a nonsensical result. ⚠️ OpenSSL prints its "Negotiated TLS1.3 group" line only for a non-default group, so a successful classical handshake reports no group at all — an earlier version of the script read that silence as failure.
  • ⚠️ One connection, one client, one day. An intercepting middlebox or a different edge changes these numbers, which is why the measurement is a script rather than a table.

The connection measured here is our own. The hybrid key exchange is our CDN's default rather than a configuration choice made by RECATOOLS, and this guide claims no credit for it.