How Encrypted Client Hello (RFC 9849) works: outer vs inner ClientHello, the DNS key, what your ISP and the CDN still see, and how to check your connection.
For years the one piece of an HTTPS connection that still named the website in plaintext was the Server Name Indication (SNI) field in the TLS handshake. Encrypted Client Hello (ECH) is the standardized fix: the browser encrypts its real ClientHello and wraps it inside a harmless-looking outer one. It is easy to oversell, though. ECH moves the secret from "everyone on the path" to "the server that decrypts it", and it leaves more of the handshake visible than most summaries suggest. This post explains the mechanism and, party by party, what each observer can and cannot read.
Key Takeaways
- ECH is now a standard. The specification was published as RFC 9849 (Proposed Standard, March 2026), after years as the draft
draft-ietf-tls-esni. - Two ClientHellos, one connection. The browser sends a readable outer ClientHello addressed to the client-facing server's
public_name, with the real inner ClientHello (including the true SNI and the ALPN list) encrypted inside it. - The key comes from DNS. The server's ECH configuration is published in an HTTPS/SVCB DNS record (RFC 9460), so ECH only protects you if that lookup is not itself readable. The RFC says plainly that SNI encryption is less useful without encrypted DNS.
- A network fingerprint of the outer hello is still possible. The outer hello is a real, readable ClientHello, and the spec lets the client copy fields from the inner one into it, so the TLS library's habits need not disappear from the wire.
- The server that decrypts sees everything. ECH changes what the network sees, not what the CDN or origin sees.
The Problem ECH Solves
TLS 1.3 encrypts the server certificate and most of the handshake, but the ClientHello has to be readable before any keys exist. That is why the SNI, which tells a server which of its many hostnames you want, travelled in plaintext. The RFC calls it "perhaps the most sensitive information left unencrypted in TLS 1.3". The older requirements discussion, RFC 8744, lists why earlier attempts to encrypt it were hard, which is why it took until 2026 for a finished standard. For the plain-language version of what SNI leaks to an ISP or Wi-Fi operator, see what your ISP can still see when you use HTTPS; this post focuses on how the fix works.
Outer and Inner ClientHello
With ECH the browser builds two messages:
- ClientHelloInner is the real handshake message: the true SNI, the ALPN list (which advertises protocols such as HTTP/2 or HTTP/3), and the other extensions. It is encrypted with a public key belonging to the server, using HPKE (hybrid public key encryption), and padded so its length leaks less.
- ClientHelloOuter is an ordinary-looking ClientHello. Its
server_nameis set to thepublic_namefrom the ECH configuration, which is the name of the client-facing server, for example a CDN's shared hostname. The encrypted inner message rides inside it in anencrypted_client_helloextension.
The client-facing server tries to decrypt the extension. If it succeeds, it continues the handshake as the inner hello and the connection proceeds to the real site. If it cannot (wrong key, or the extension was a dummy), it treats the outer hello as the whole story and proceeds as the public_name. The spec describes two layouts: shared mode, where one provider is both client-facing server and origin, and split mode, where the client-facing server forwards the decrypted hello to a separate backend server.
One consequence the RFC states directly: ECH "reveals that a client is connecting to a particular service provider, but does not reveal which server from the anonymity set terminates the connection". If a site is the only tenant behind its IP address, the destination IP still gives it away. ECH helps in proportion to how many sites share the front door.
Where the Key Comes From: DNS
The browser needs the server's public key before it connects, and it gets it from DNS. The server publishes an ECHConfigList (one or more ECHConfig entries containing the key, a config ID and the public_name) in an HTTPS or SVCB DNS record, the record type defined in RFC 9460.
That creates a dependency chain. If your DNS query for the HTTPS record travels in plaintext, your resolver, and anyone on the path, learns the hostname before ECH has hidden anything. The RFC notes that encrypted DNS is also a defense against local-network attackers tampering with the ECH configuration, and that SNI encryption is "less useful without encryption of DNS queries in transit". In practice that means DNS over HTTPS or DNS over TLS. DNS Leak Prevention covers how to set that up and test it.
Who Sees What: The Three-Observer Table
| On-path network (ISP, Wi-Fi) | Client-facing server (e.g. a CDN) | Origin behind it | |
|---|---|---|---|
| Destination IP | Yes | Yes | Yes |
Outer SNI (public_name) | Yes | Yes | Split mode: no |
| True SNI and inner ALPN | No (encrypted) | Yes, after decrypting | Yes |
| Other fields copied into the outer hello | Yes | Yes | n/a |
| Full inner ClientHello | No | Yes | Yes |
| Plaintext DNS query, if used | Yes | n/a | n/a |
Two rows deserve emphasis. First, the true SNI is hidden from the network but never from the party that terminates the connection. ECH is not anonymity: the CDN still knows which of its customers you visit. Second, the "other fields" row is the part that surprises people.
What a Network JA3/JA4 Can Still Read
A common shorthand, which our TLS fingerprinting pillar also uses, is that ECH leaves the observer with only a minimal outer ClientHello that limits fingerprinting. The RFC supports the first half and is more cautious about the second. When the client builds the outer hello, it follows a short list of rules (offer TLS 1.3 or above, copy the session ID, use a fresh random, put the public_name in server_name) and then, per the text, "it MAY copy any other field from the ClientHelloInner except ClientHelloInner.random". The spec's own anonymity-set discussion adds that implementation-specific choices, such as extension ordering within TLS messages, can produce different externally visible behavior even when configurations match.
So the outer hello is a genuine ClientHello: it has cipher suites, extensions, supported groups and an ordering, all readable on the wire. A passive observer can compute a JA3 or JA4-style fingerprint of that message. What changes is which hello the fingerprint describes and which fields are hidden: the real SNI and the inner ALPN list are no longer part of what the observer sees, and the SNI-presence and ALPN values that JA4 records are taken from the outer hello. Whether the outer fingerprint is as distinctive as today's is a property of each browser's implementation, not something the RFC guarantees either way. Treat "ECH defeats network TLS fingerprinting" as unproven, and "the observer reads less" as the accurate statement. (The same applies to QUIC: see HTTP/3 and QUIC fingerprinting.)
The server side is different. The client-facing server decrypts the inner hello and sees the complete client fingerprint, which is why bot-detection vendors working at a CDN lose nothing from ECH.
GREASE ECH: Why the Extension Proves Nothing
A browser that has no ECH configuration for a site still sends an encrypted_client_hello extension, filled with random bytes of a plausible length. The RFC calls this GREASE ECH (the same idea as the dummy values in RFC 8701). It exists so that real ECH does not stand out and middleboxes cannot cheaply block "the ECH extension" without also breaking ordinary connections. A server that cannot decrypt it simply ignores it.
The practical lesson is that seeing the extension in a packet capture tells you nothing about whether the connection was actually protected. Only a successful handshake with an accepted ECH does. The spec is also candid that GREASE and real ECH are designed to be indistinguishable only to a limited passive observer; values such as the outer SNI or the config ID remain possible differentiators.
Failure Modes: Retries, Fallback and Blocking
- Stale key and retry_configs. DNS caches and key rotation mean the browser's key can be out of date. If the server cannot decrypt, it completes the handshake with the outer hello and returns fresh
retry_configsin an encrypted message. The client verifies the certificate against thepublic_name, then retries with the new configuration. The spec tells clients not to accept a secondretry_configsin response to a retry, since that signals a misconfigured server. ech_required. If the client offered real ECH and the connection ends up without it, the client aborts before sending application data, rather than quietly falling back to a plaintext SNI.- No config at all. If the DNS record has no ECH parameter (or the HTTPS record is stripped), the browser has nothing to encrypt with and connects the old way, with GREASE if it supports it. Whether a given browser then shows an error or proceeds is a product decision, so expect ECH to be best-effort.
- Networks that object. The RFC acknowledges that some operators may block real ECH while allowing GREASE, and that managed environments that rely on the SNI for filtering or load balancing may disable ECH by policy. It does not claim ECH is unblockable, only that it is designed to be hard to tell apart from GREASE for a passive observer.
No adoption percentage is given here, because a single number depends on which sites and clients you count. Cloudflare's write-up is the place to read how a large CDN rolled it out and what it changes for operators.
How to Check Your Own Connection
Any site served through Cloudflare exposes a diagnostic at /cdn-cgi/trace. Open it and look for the sni= line: sni=encrypted means the connection used ECH, and sni=plaintext means the true server name crossed the network in the clear. When we requested browserinsight.net/cdn-cgi/trace from a non-ECH client on 2026-10-12 it returned sni=plaintext, which is the expected result for a client without a published key or without ECH enabled. That check only covers Cloudflare-served sites, and BrowserInsight's own tools do not detect ECH.
Get the preconditions right and the result improves: use a browser that supports ECH, enable encrypted DNS, and visit a site whose operator has published an HTTPS record with an ECH configuration. For context on what remains exposed even then, the IP intelligence check shows the address a server sees, and the destination IP stays visible to the network regardless of ECH.
Frequently Asked Questions
Does ECH hide which website I visit from my ISP?
Partly. It hides the true server name in the handshake, but your ISP still sees the destination IP address and the outer public_name. For sites that share an IP with many others, such as those behind a large CDN, that leaves real ambiguity; for a site on its own IP, the address alone identifies it.
Does ECH replace a VPN?
No. A VPN moves the observer's position: your ISP sees only the VPN, and the VPN provider sees what the ISP would have seen. ECH hides one field from the network and still leaves the destination IP, traffic timing and volume visible.
Does ECH work without encrypted DNS?
It can connect, but the privacy benefit is weakened. The key is fetched from a DNS record, and a plaintext lookup reveals the hostname to the resolver and the path before the handshake starts.
Can a website tell whether I used ECH?
Yes. The client-facing server knows whether it decrypted an ECH extension, and a CDN exposes that, as the sni= line in its trace output shows. The inner ClientHello is fully visible to that server.
Is the extension in a packet capture proof that ECH was used?
No. Browsers send GREASE ECH when they have no configuration, so the extension can be present on connections that were not protected.


