// Engineering Log
HTTP, HTTPS, and TLS: Part 6 — What is ECH (Encrypted Client Hello) and How It Hides the Site Name
Published on 2026-10-07
// Fast route
This article belongs to the topic Servers and infrastructure.
ECH (Encrypted Client Hello) — a TLS 1.3 extension that encrypts the first handshake message, ClientHello. The main thing it hides is the site name in the SNI field. Without ECH any node between you and the server can see that you opened shop.example.ru, even if everything else is encrypted. With ECH an observer sees only a common name, the same for many sites behind one server or CDN. The standard was published in March 2026 as RFC 9849, and the way to publish keys in DNS — as RFC 9848.
What remains exposed without ECH
The TLS 1.3 handshake encrypts almost everything, including the server certificate. But the ClientHello is sent before the parties agree on keys, so it is plaintext and contains:
- SNI — the site name;
- ALPN — the list of protocols (
h2,http/1.1), which reveals the application type; - a set of ciphers, groups and extensions used to build client fingerprints (JA3, JA4).
SNI is what DPI equipment, corporate network filters and ISP parental controls use to block sites. Previously there was an attempt to encrypt only SNI (ESNI, 2018), but it left other fields exposed and never became a standard. ECH is its replacement: the entire ClientHello is encrypted.
How it works
Two ClientHellos in one. The client constructs:
- an inner ClientHello — the real one, with the desired site name and full ALPN list;
- an outer ClientHello — public, with a common name (
public_name), for examplecloudflare-ech.com. The inner one is embedded in it encrypted in theencrypted_client_helloextension.
Everyone sees the outer ClientHello; only the server that holds the ECH private key can read the inner one.
Where the client gets the key. The server publishes an ECH configuration (ECHConfigList: public key, algorithms and public_name) in an HTTPS DNS record for its domain:
dig +short HTTPS example.ru
1 . alpn="h3,h2" ipv4hint=192.0.2.10 ech=AEX+DQBBpQAgACB...The browser queries this record along with A/AAAA and, if it contains the ech parameter, encrypts the ClientHello. Encryption uses HPKE (RFC 9180): the client derives an ephemeral key from the server’s public key.
What the server does. It decrypts the inner ClientHello and continues the handshake with it as if the outer one never existed. In the response the server quietly confirms to the client that ECH was accepted.
If the key is stale. ECH keys are rotated periodically. If the client encrypted the ClientHello with an old key, the server cannot decrypt it and will complete the handshake using the outer ClientHello — with a certificate for the public_name — and will provide the client with the current configuration (retry configs). The client reconnects with the new key. Therefore the server needs a valid certificate for the common name as well.
Two deployment modes:
- shared mode — the same server both decrypts ECH and serves sites; Cloudflare works this way and Nginx supports ECH in this mode;
- split mode — a separate front-end only decrypts the ClientHello and passes the connection to the site server without seeing its contents. Suitable when the front and the sites belong to different owners.
GREASE ECH. ECH-capable browsers send the encrypted_client_hello extension with random data even to sites that don’t have ECH. This prevents distinguishing a real ECH-capable connection by the presence of the extension and prevents middleboxes from failing on an unknown extension.
What ECH hides and what it doesn’t
ECH hides the site name from a passive observer, but only under several conditions:
- DNS is also encrypted. If the
example.ru HTTPSquery goes via plain DNS, the name is visible there. ECH makes sense together with DNS-over-HTTPS or DNS-over-TLS. Firefox uses ECH only when DoH is enabled. - Many sites are behind the address. If there is only one site at the IP address, the name is revealed by the address itself. Therefore ECH truly works at CDNs and large hosters where thousands of domains share one address.
- The observable connection features. Response length, timing, presence of the extension — all remain available for traffic analysis.
ECH does not hide the server’s IP address and is not a means to bypass blocks: the fact of using ECH or the provider’s address can be blocked.
Where ECH works today
- Browsers: Chrome since version 117 and Firefox since version 119 (2023) use ECH by default if they find a configuration in DNS.
- Cloudflare enabled ECH for sites on free plans in 2023, disabled it due to issues and began rolling it out again in September 2024. Its
public_nameiscloudflare-ech.com. - OpenSSL supports ECH since version 4.0 (April 2026). Previously ECH existed only in a separate OpenSSL branch, in BoringSSL (Chrome) and NSS (Firefox).
- Nginx gained ECH in version 1.29.4 (the
ssl_ech_filedirective, shared mode), when built with an OpenSSL that supports ECH. - curl — the
--echoption since 8.8.0 as an experimental feature, when built with a TLS library that supports ECH.
ECH in Russia
On November 5–6, 2024 TSPU began blocking connections to Cloudflare that contained the ECH extension — along with the public_name cloudflare-ech.com. Roskomnadzor explained that using ECH violates Russian legislation and recommended site owners not to use the Cloudflare CDN. In practice, users with ECH enabled stopped being able to open many Cloudflare-hosted sites unrelated to politics.
For a site owner with an audience in Russia this implies:
- if the site is behind Cloudflare and some visitors report inaccessibility, check whether ECH is enabled in SSL/TLS → Edge Certificates (on the free plan the toggle is not always available — then use the API or support);
- enabling ECH on your own server for a Russian audience makes no sense: there is no benefit in hiding the name, and there is a risk of the connection being blocked.
How to enable ECH in Nginx
You need Nginx 1.29.4 or newer and OpenSSL 4.0 or newer. Prebuilt packages are still rare, so Nginx is usually built from sources with the option --with-openssl=<path-to-OpenSSL-4-source>.
- Generate the ECH key. The file contains the private key and the configuration to publish in DNS:
openssl ech -public_name ech.example.ru -out /etc/nginx/ech/ech.example.ru.pem.echpublic_name is the common name an observer will see. It needs a regular certificate: it is used when the client connects with an old key.
- Attach the key. It’s convenient to set the directive at the
httplevel so it applies to all sites:
http {
ssl_ech_file /etc/nginx/ech/ech.example.ru.pem.ech;
log_format ech '$remote_addr "$request" $status '
'ech=$ssl_ech_status outer=$ssl_ech_outer_server_name sni=$ssl_server_name';
server {
listen 443 ssl;
server_name ech.example.ru;
ssl_certificate /etc/letsencrypt/live/ech.example.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ech.example.ru/privkey.pem;
return 404;
}
server {
listen 443 ssl;
server_name shop.example.ru;
ssl_certificate /etc/letsencrypt/live/shop.example.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/shop.example.ru/privkey.pem;
access_log /var/log/nginx/shop.access.log ech;
root /var/www/shop;
}
}The variable $ssl_ech_status takes values SUCCESS (ECH accepted), NOT_TRIED (the client didn’t try), GREASE (the client sent a GREASE dummy), FAILED and BACKEND.
- Publish the configuration in DNS. Add the Base64 string from the
.pem.echfile (theECHCONFIGblock) to the HTTPS record of each site:
shop.example.ru. 300 IN HTTPS 1 . alpn="h2,http/1.1" ech="AEX+DQBBpQAgACB..."Not all DNS hosts allow creating HTTPS records; if yours doesn’t, ECH will not work.
- Rotate keys. ECH keys should be refreshed every few weeks: generate a new one, add it to Nginx alongside the old one (the directive can be repeated), publish it in DNS, wait for the TTL to expire, then remove the old key.
How to check
In the browser. The page https://crypto.cloudflare.com/cdn-cgi/trace shows sni=encrypted if ECH worked, and sni=plaintext if not. On your own site the result is visible in logs via the $ssl_ech_status variable.
Via curl (requires a build with ECH):
curl --ech true https://shop.example.ru/ # try ECH, if it fails — fall back
curl --ech hard https://shop.example.ru/ # only ECH, otherwise error
curl --ech ecl:AEX+DQBBpQAgACB... https://shop.example.ru/ # config manually, without DNS
curl --ech false https://shop.example.ru/ # do not use ECHcurl fetches the configuration from the HTTPS record via DoH, so --doh-url https://dns.example.net/dns-query is usually specified together with --ech.
Via openssl (4.0 and newer):
openssl s_client -connect shop.example.ru:443 -servername shop.example.ru \
-ech_config_list AEX+DQBBpQAgACB...From the network. In Wireshark a connection with ECH will show the SNI as the public_name, and the ClientHello will contain the encrypted_client_hello extension (type 0xfe0d) with a large data block.
Common errors
- ECH is enabled, but the client’s DNS is plain — the site name is still visible in DNS queries.
- No certificate for the
public_name— clients with an old key cannot obtain the new one and drop out. - The key was changed on the server but not updated in DNS, or vice versa — clients get errors until TTL expires.
- ECH on a separate server with a single site: the name is not visible in SNI, but is easily determined by IP address.
- ECH enabled for a site with a Russian audience — some visitors lose access due to blocking.
// Similar task
If you are dealing with something similar
This article belongs to one of the main working topics. You can keep reading on the topic, go to the homepage to understand what I do, or open the service pages directly.
Article topic
Servers and infrastructure
VPS, Linux, web stack, migrations, hosting, databases, and core operations.
Typical tasks behind this topic
- Move a site or service to a new server
- Set up Linux, Nginx, databases, and backups
- Figure out why the system behaves unstably
// Next step
If you need help with this topic, not just another article, it is better to go straight to the service page. The homepage and topic collection stay available as secondary routes.
Open services// Reviews
Related reviews
Thanks to Mikhail for his responsiveness. We spoke by phone; he explained how I could do it myself. This is my second time reaching out — everything’s great and prompt.
Thanks to Mikhail for his responsiveness. We had a call; he explained how I could do it myself. This is my second time contacting him; everything is great and prompt.
Consultation on Nginx Proxy Manager and Portainer
2025-02-25 · ★ 5/5
I want to express my deep gratitude to the specialist who set up SEO-friendly URLs for me on OpenCart. Configuring them turned out to be easy and simple, and I'm glad I finally found a professional who did everything well and without unnecessary complications. Before this I went through four specialists, and each time there were issues with the setup, but this person handled the task perfectly.
I want to express my great gratitude to the specialist who configured SEO-friendly URLs for me on OpenCart. Setting up the URLs turned out to be easy and simple, and I'm glad that I finally found a professional who did …
Excellent work — this isn't the first time I've used them; they find solutions to complex problems. I recommend.
Excellent work, this isn't my first time contacting them; they find solutions to complex problems. I recommend.
Excellent work! He completed the assigned task on time and without errors. It was a pleasure to work with him; I recommend him.
Great job! Completed the assigned task on time and without errors. It was a pleasure to work together, I recommend.
Excellent specialist — he delved into the problem, figured it out, and fixed it. I recommend him.
Excellent specialist, delved into the problem, figured it out, and fixed it. I recommend.
I needed to resolve an SSL certificate issue on a server where the certificate had been issued through Nginx Proxy Manager. Mikhail clarified all the details of my setup and requested access to assess the feasibility of solving the issue, since he hadn't worked with that service before. He quickly figured it out and fixed my problem. Perfect cooperation)
I needed to solve an issue with an SSL certificate on the server that was issued through Ngnix Proxy manager. Mikhail clarified all the details of how everything is set up for me, asked for access to assess the …
Diagnosing Nginx Proxy Manager in a Docker container and resolving the issue
2024-03-15 · ★ 5/5
// Contact
Need help?
Get in touch with me and I'll help solve the problem
I reply within one business day (03:00-13:00 GMT)
Или оставьте заявку здесь:
// Related