ECH Fronting?
Hello everyone, I recently discovered a unique use for ECH.
China is currently blocking all overseas DOH public services, making ECH difficult to use.
My tests have shown that all websites hosted on the CF Free plan have the same ECH value when querying HTTPS records at the same time.
So, you can even use your local DNS to query the HTTPS records of another CF website that hasn't been DNS poisoned in plaintext to obtain the ECH public key, and then use it normally.
This method is great! It doesn't rely on DOH or overseas DNS services.
Will the GFW pollute the HTTPS records of all websites?
Xray currently has this feature, but it's for domains with proxy functionality.
This method doesn't actually require a proxy server and is very suitable for browser plugins. I'm wondering if any developers would be willing to implement it? Unblocking CF websites with ECH in one click.
Xray文档 https://xtls.github.io/config/transport.html#tlsobject 特别地,可以使用指定的域名用于查询 ECHConfig, 格式为 "example.com+https://1.1.1.1/dns-query" 这样 Xray 会强制使用 example.com 的 DNS 记录中的 ECHConfig 用于连接,如果你想从 DNS 获取 ECHConfig 但又不想暴露自己在查询这个域名的 HTTPS 记录或者在这个域名下发布 HTTPS 记录时有一些用。
I've been always using ech key of cloudflare-ech.com for other cloudflared domains. Querying cloudflare-ech.com.cdn.cloudflare.net, this suffix hack can also workaround dns hijacking.
Edit: the suffix hack works for circumventing A and AAAA hijacking but not HTTPS. It doesn't have ECH key currently.
Xray currently has this feature, but it's for domains with proxy functionality.
This method doesn't actually require a proxy server and is very suitable for browser plugins. I'm wondering if any developers would be willing to implement it? Unblocking CF websites with ECH in one click.
https://github.com/net4people/bbs/issues/454 Xray-core 可以对浏览器的连接 MITM,Dirrect/Freedom 出站 SNI 填写 "fromMitm",并结合 ECH 即可实现你想要的效果
#454 Xray-core can perform MITM on browser connections. For Dirrect/Freedom outbound SNI, set it to “fromMitm”. Combined with ECH, this achieves the desired effect.
当然可能你得先用 HTTP 入站然后过一遍路由筛选出 CF 的域名再转到 Tunnel/Dokodemo 入站,如果你弄出了完整配置可以分享出来
就像 https://github.com/XTLS/Xray-examples/tree/main/Serverless-for-Iran
Of course, you might need to first use HTTP inbound traffic, then filter out CFe's domain through routing before redirecting to Tunnel/Dokodemo for inbound access. If you manage to set up a complete configuration, feel free to share it.
Just like https://github.com/XTLS/Xray-examples/tree/main/Serverless-for-Iran
真的可以哎,照着上面的配置改了改 似乎HTTPS记录中没有echconfig值的Cloudflare网站都支持ECH,这下全部解锁直连了 正好搭配CF workers vless,workers不能连CF的IP,可以直连
如果浏览器插件可以处理就不用MITM了,看了下以前的讨论似乎现在浏览器做不到
It really works! I tweaked the settings based on the instructions above. It seems all Cloudflare sites without an echconfig value in their HTTPS records now support ECH. This has unlocked direct connections for everything. Perfect for pairing with CF Workers VLess—since Workers can't connect to CF IPs, direct connections are now possible.
If browser extensions could handle this, we wouldn't need MITM. From past discussions, it appears browsers currently can't manage it.
Update (2025-09-30): Added DoH-ECH support and extra annotations.
// CF ECH MitM Proxy + CF Worker VLESS
// Xray-core v25.9.11+
// Requires a self-signed certificate: you can create one using the command "xray tls cert -ca -file=mycert".
// Additionally, you must import the certificate into the system/browser "Trusted Root Certification Authorities".
{
"log": {
"loglevel": "none",
"dnsLog": false,
"access": "none"
},
"dns": {
"tag": "dns-query",
"hosts": {
"cloudflare-dns.com": "1.1.1.1", // placeholder — do not resolve itself, match rules directly
"one.one.one.one": "1.1.1.1"
},
"servers": [
"https://cloudflare-dns.com/dns-query", // DoH will force ECH
"https://one.one.one.one/dns-query"
],
"disableFallback": true
},
"inbounds": [
{
"tag": "socks-in",
"port": 1080,
"protocol": "socks",
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
],
"routeOnly": false
}
},
{
"tag": "tls-decrypt",
"protocol": "tunnel",
"listen": "127.0.0.1",
"port": 44443,
"settings": {
"port": 443,
"network": "tcp",
"followRedirect": true
},
"streamSettings": {
"security": "tls",
"tlsSettings": {
"alpn": [
"h2",
"http/1.1"
],
"certificates": [
{
"usage": "issue",
"certificateFile": "mycert.crt", // self-signed CA certificate — place it at the specified path
"keyFile": "mycert.key" // self-signed CA private key — place it at the specified path
}
]
}
}
}
],
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{
"inboundTag": [
"socks-in"
],
"outboundTag": "redirect-out",
"network": "tcp",
"protocol": [
"tls"
],
"port": 443,
"ip": [
"geoip:cloudflare"
]
},
{
"inboundTag": [
"dns-query"
],
"outboundTag": "redirect-out"
},
{
"inboundTag": [
"tls-decrypt"
],
"outboundTag": "tls-repack",
"ip": [
"geoip:cloudflare"
]
},
{
"inboundTag": [
"socks-in"
],
"balancerTag": "worker-balance", // If not using CF Workers, you can remove worker-xx from outbounds and change this to direct
"ip": [
"geoip:!cloudflare"
]
}
],
"balancers": [
{
"tag": "worker-balance",
"selector": [
"worker"
],
"strategy": {
"type": "random"
}
}
]
},
"outbounds": [
{
"tag": "worker-1",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "###########", // fill in preferred CF IP
"port": 443,
"users": [
{
"id": "###########", // fill in your UUID
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"wsSettings": {
"host": "###########.workers.dev", // workers domain supports ECH, so you can put the workers domain here
"path": "/?ed=2560"
},
"security": "tls",
"tlsSettings": {
"serverName": "###########.workers.dev", // workers domain supports ECH, so you can put the workers domain here
"allowInsecure": false,
"echConfigList": "gitlab.io+udp://1.1.1.1", // echConfigList should point to a domain that can fetch CF's ECH config + a DNS that can resolve it successfully
"echForceQuery": "full",
"fingerprint": "chrome"
}
}
},
{
"tag": "worker-2", // same as above, you can duplicate with different workers or preferred IPs; tag them worker-xx, the balancer will load-balance
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "###########",
"port": 443,
"users": [
{
"id": "###########",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"wsSettings": {
"host": "###########.workers.dev",
"path": "/?ed=2560"
},
"security": "tls",
"tlsSettings": {
"serverName": "###########.workers.dev",
"allowInsecure": false,
"echConfigList": "gitlab.io+udp://1.1.1.1",
"echForceQuery": "full",
"fingerprint": "chrome"
}
}
},
{
"tag": "block",
"protocol": "blackhole"
},
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "ForceIP"
}
},
{
"tag": "redirect-out",
"protocol": "freedom",
"settings": {
"redirect": "127.0.0.1:44443"
}
},
{
"tag": "tls-repack",
"protocol": "freedom",
"settings": {
"redirect": "###########:443", // If you remove this line, direct connections to CF domains will use DNS-resolved IPs; if you set a preferred CF IP here, it will connect to that IP
"domainStrategy": "ForceIP"
},
"streamSettings": {
"security": "tls",
"tlsSettings": {
"serverName": "fromMitM",
"verifyPeerCertInNames": [
"fromMitM"
],
"alpn": [
"fromMitM"
],
"allowInsecure": false,
"echConfigList": "gitlab.io+udp://1.1.1.1", // echConfigList should point to a domain that can fetch CF's ECH config + a DNS that can resolve it successfully
"echForceQuery": "full",
"fingerprint": "chrome"
}
}
}
]
}
Querying cloudflare-ech.com.cdn.cloudflare.net, this suffix hack can also workaround dns hijacking.
It seems that the HTTPS record of this domain does not have the ECH key, but cloudflare-ech.com does. Maybe CF is adjusting something?
I discovered another interesting use case for DoH-ECH-Fronting.
This is a chicken-and-egg question.
cloudflare-dns.com is blocked by the GFW using SNI.
You can forcefully connect to cloudflare-dns.com using another website's ECH config, even though cloudflare-dns.com's HTTPS record doesn't have an ECH config.
It actually works. This allows you to successfully use the DoH service, but this only applies to Cloudflare DoH.
Amazing Cloudflare.
Currently, only Xray MITM Proxy can achieve this.
Or you can set up a DoH service yourself and modify the HTTPS records.
You can also modify your browser, but I can't do this method personally.
Perhaps various anti-censorship developers could consider whether this approach is worthwhile?
~~I wonder if this discussion will accelerate the GFW's blocking of the outer SNI cloudflare-ech.com?~~
~~However, based on the GFW leaks, it seems they do consider the socioeconomic costs when targeting CF.~~
I wonder if this discussion will accelerate the GFW's blocking of the outer SNI cloudflare-ech.com?
im surprised it isn't blocked, it was quickly blocked in russia (faster than in turkmenistan lol)
im surprised it isn't blocked, it was quickly blocked in russia (faster than in turkmenistan lol)
This special method of ECH should be effective for the whole world, not just countries with GFW. Perhaps because of the collateral damage or economic and social impact, which leads to the Chinese GFW is a little lazy in this regard, and will take action when ECH traffic is large......
collateral damage or economic and social impact
There is no economical damage in partially blocking (not even fully) cloudflare-ech.com outer sni while leaving normal tls intact, how it's done in russia. Even if you fully blocked it, it wouldn't be noticeable, it's too centralized and irrelevant to rest of cloudflare
As discovered by Mücke et al. (https://dl.acm.org/doi/pdf/10.1145/3744969.3748401)
It is also possible to discover ECH configurations of TLS servers by handshaking with them using a non-valid ECH extension (https://www.ietf.org/archive/id/draft-ietf-tls-esni-25.html#section-6.1.6).
Similar to your proposal, it is possible to discover Cloudflare's shared ECH configuration through a TLS handshake with a non-censored domain.
This is more complex than a DNS request, as the ECH configurations have to be read from the Encrypted Extensions message in TLS, but it could serve as an option facing heavy DNS censorship.
Interestingly, Mücke et al. discovered that Facebook supports ECH but does not advertise its configurations over DNS; they only advertise them in TLS handshakes.
I attached a screenshot of Facebook configurations and their hex data (each configuration starts with 0xfe0d).
fe0d003b00002000204a4cb11e151c5fee20bbc0876245adae997fb23ed21e8251aa04feed7eb80577000400010001640c66616365626f6f6b2e636f6d0000fe0d0041050020002084a6291df5ecb58303bf5e937795c4e827fc331fb42561f6370d956273288f02000400010001321267726170682e66616365626f6f6b2e636f6d0000fe0d00440800200020686a00a25e73001d3a6cb36519f9f5bf37fc117e545dc2706c89c6ba6ee9fc36000400010001321573636f6e74656e742e78782e666263646e2e6e65740000fe0d00410900200020cfd289098d162d921e405843dc8ea03567dc569f6264138f6a8fb9fe8306371a0004000100013212766964656f2e78782e666263646e2e6e65740000fe0d004401002000201d77eb1c522d08605b179d4214ee4a3635df7e17c336ea9006655a73fcaad63e00040001000164156563682d7075626c69632e61746d6574612e636f6d0000fe0d00410300200020b64aea2668a850964bac58c4225240a3bb3f3ed1647a8f20b40c8b00353ea0010004000100016412766964656f2e78782e666263646e2e6e65740000fe0d0048070020002047f15b35f607370c80df4c2869dfe1ba1fbd4a8af658e1db7078a6b30ae808420004000100013219766964656f2d6c6178332d322e78782e666263646e2e6e65740000fe0d004b0600200020b62e785da0fc561c06126c631a96b1524b80cf7566a5048c3854918123dc851d000400010001321c73636f6e74656e742d6c6178332d322e78782e666263646e2e6e65740000fe0d00480400200020d412f9c7954d6af3f677df04cd7adfe9efa59f64e45e562b9d6de030061b745e0004000100016419766964656f2d6c6178332d322e78782e666263646e2e6e65740000fe0d00380200200020fedede9515c3b0e1381d71686392ef8c5a66e1ab954cc9a3e0dd31e1d2264d480004000100016409666263646e2e6e65740000
All configurations share their properties except for the outer SNI.
seems not work with x.com handshake is complete, but cf return a 403 with plaintext like IPv6
seems not work with x.com handshake is complete, but cf return a 403 with plaintext like IPv6
Currently, twitter only allows access from IPv4 addresses.