Dev.to Security 🔐 Cybersecurity 👁 0 📖 3 min read

Your browser speaks post-quantum. The server behind it probably doesn't.

Chrome, Firefox and Safari all negotiate X25519MLKEM768 by default now. Cloudflare Radar puts the post-quantum share of HTTPS traffic at around 72%. So we're done? Not really. That number is the hop from your browser to

Chrome, Firefox and Safari all negotiate X25519MLKEM768 by default now. Cloudflare Radar puts the post-quantum share of HTTPS traffic at around 72%. So we're done?

Not really. That number is the hop from your browser to the CDN edge. The hop from the edge to the origin, the server someone actually runs, is a separate TLS handshake with a separate TLS stack. Radar's own number for that hop is under 20%.

I wanted to know why, so I built a tool to work it out and pointed it at a lot of servers.

What I measured

Self-run origins (no big CDN in front), counted per (hostname, IP) pair, the same way Radar counts:

  • Tranco ranks 10k–1M (sample): 22.2% post-quantum (95% CI 19.8–24.5)
  • Tranco top 10k: 23.3%
  • Top 10k with CDNs counted (what a visitor actually lands on): 66.1%

The 22% lined up almost exactly with Radar's 19.6% for customer origins, which was a nice sanity check that I wasn't measuring garbage.

Why they aren't post-quantum

Of the 3,327 non-PQ origins in my top-10k run:

  • 60% speak TLS 1.3 but don't offer ML-KEM
  • 34% don't speak TLS 1.3 at all
  • 3% have a hand-written curve list pinned to P-256/P-384

The part that surprised me came when I fingerprinted the TLS library behind each one. Only 13 of those 3,327 were provably a config problem. The rest:

  • Library (1,137): OpenSSL 3.0–3.4 and 1.1.1 dominate, plus Go older than 1.24. None of these have ML-KEM, so no config line will help.
  • Provider (759): AWS load balancers (336), F5 BIG-IP (179), and a long tail. 545 of them sit behind a product that already has a PQ setting that nobody turned on.
  • Unknown (1,381): mostly TLS 1.2-only servers, which never send the reply that gives the library away.

(The 13 is a floor. A server only counts as "config" with positive proof its library has ML-KEM, and a hand-set group list hides that.)

So "add the PQ group to your config" is the wrong advice for most servers. OpenSSL below 3.5 can't do it. You upgrade, or you put something in front that can: OpenSSL 3.5+, Go 1.24+, Caddy 2.10+, or cloudflared.

If you are on OpenSSL 3.5+ with nginx and still not PQ, look for an old curve line:

ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1;

How you check without finishing a handshake

TLS 1.3 negotiation happens in the clear, so you can learn a lot from the server's first reply alone.

The tool builds every ClientHello by hand and sends seven probes, each changing one variable. The key shares are valid but inert: the X25519 base point, the P-256 generator, and an ML-KEM key made of zero coefficients plus a seed, which still passes FIPS 203's modulus check. The server replies with a ServerHello or a HelloRetryRequest, and that tells you what it accepts. The tool never sends Finished and never sends application data.

One pair of probes is my favourite. A PQ ClientHello is about 1.4 KB versus about 250 bytes for a classical one, and some middleboxes choke on the big one. So one probe sends the PQ hello split at byte 200, and a control probe sends a classical hello padded to the same size and split the same way. If both fail, a middlebox is dropping big hellos. If only the PQ one fails, the server is rejecting the group.

A weird one: Google Cloud flapping

Between two runs ten days apart, 15 origins appeared to lose post-quantum. None of them actually regressed. I re-probed each one 11 times over 15 minutes, and 14 of them flapped: the same IP answered with ML-KEM on some probes and without it on others, minutes apart. Those 14 were all Google Cloud addresses, and Google Cloud's PQ share in my sample went from 15% to 43% across those same ten days. It looks a lot like a staged ML-KEM rollout on their load balancers.

The lesson: confirm a regression with a second probe before you alert on it. The tool now reports a mixed verdict for these instead of crying wolf.

Try it

It's called pqdoctor, an MIT-licensed Rust CLI. It checks TLS, mail (STARTTLS) and SSH servers, tells you why they aren't post-quantum, and prints the fix for that server's software.

cargo install pqdoctor
pqdoctor yourdomain.com

It's also on Homebrew and Scoop, and there's a GitHub Action that fails your build if an origin falls off PQ. Docs and an FAQ are at pqdoctor.com.

Caveats: one vantage point, IPv4 only, and these are servers comparable to Cloudflare's origins, not Cloudflare's actual origins. What transfers is the mix of causes.

If you run your own origin, what's in front of it, and is it post-quantum yet? I'm curious what people find.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.