mhr-cfw: The Proxy That Hides Behind Google, Then Escapes Through Cloudflare
A deep dive into a two-hop domain-fronting relay that turns Google Apps Script and Cloudflare Workers into a censorship workaround, with HTTP/2 multiplexing, SNI rotation, and latency tricks that make the disguise usable.
- mhr-cfw is built around relay shape, not simple proxying: the visible request lands on Google first, then escapes through Cloudflare.
- Its main defensive value is cost imposition, because blocking one hop risks collateral damage to services that people expect to keep working.
- The project is as much latency engineering as censorship evasion, using HTTP/2 multiplexing and parallel relay racing to make a slow path usable.
- The design is powerful precisely because it is brittle to deploy, which makes it a specialist tool for hostile networks rather than a general-purpose proxy.
The proxy that looks like Google
The unusual thing about mhr-cfw is not that it proxies traffic. It is that the proxy is deliberately split across surfaces that look ordinary to a censor. A local client talks to a local proxy, the proxy disguises the request as Google-bound traffic, Google Apps Script forwards it, and Cloudflare Worker finishes the job.
That shape matters more than the individual hops. If you only look for a single exit node, you miss the point. The system is trying to make blocking decisions expensive, politically awkward, and technically messy.
A Domain-Fronting Relay that routes traffic though GAS (Google Apps Script) and forwards it to Cloudflare Workers. Designed to bypass DPI.
Why this shape is hard to block
Single-hop relays are easier to reason about and easier to ban. This project adds a second cloud boundary, which means the censor is no longer staring at a neat single target. It is choosing between broad collateral damage and a narrower block that may fail to catch the real path.
| Option | Blocking risk | Setup burden | Performance | Fit for hostile networks |
|---|---|---|---|---|
| Worker-only proxy | One provider can be targeted directly | Low | Usually good | Moderate |
| GAS-only proxy | Google surface can be targeted or quota-limited | Low to medium | Often uneven | Moderate |
| mhr-cfw | Two huge providers must be handled together | High | Better than it sounds after optimization | High |
| Tor Meek | Depends on Tor fronting infrastructure | Medium to high | Variable | High |
| GoodbyeDPI | No remote provider to block | Low | Fast locally, limited by network model | Medium |
How the local proxy works
The local side is practical rather than glamorous. `proxy_server.py` handles proxying, while `mitm.py` generates certificates on the fly so HTTPS traffic can be decrypted, repackaged, and forwarded. That MITM step is not optional. You cannot front encrypted traffic you cannot inspect.
The local cache matters too. Static assets are expensive to relay repeatedly, so the proxy keeps common responses around instead of paying the full path cost every time. That is the difference between a proof of concept and a tool someone can actually live with.
The relay engine is the real invention
`domain_fronter.py` is where the project stops being a wrapper and becomes an engine. It rotates SNI values, races multiple Apps Script endpoints in parallel, and takes the fastest successful relay. The goal is not just to pass traffic. It is to shorten the ugly tail that usually makes censorship work feel unusable.
Why HTTP/2 matters here
`h2_transport.py` is the efficiency layer that keeps the whole design from collapsing under its own ceremony. A persistent TLS tunnel carrying many requests changes the economics of the connection. You are not paying the handshake tax over and over, and you are not advertising a new connection pattern for every asset.
That is why the project feels more engineered than improvised. The obvious trick is domain fronting. The less obvious trick is making the disguised path behave enough like a normal service session that it can survive real use.
The cloud-side relay chain
On the cloud side, `Code.gs` and `worker.js` split the work. Google Apps Script batches and forwards. Cloudflare Worker fetches the target, handles binary data with Base64, and guards against loops with relay-hop checks. The result is a chain that can absorb traffic without forcing every stage to understand everything.
| Layer | Job | Why it exists |
|---|---|---|
| Google Apps Script | Batch, validate, forward | It gives the traffic a Google-shaped front door. |
| Cloudflare Worker | Fetch the target and return bytes | It provides a separate exit that is hard to isolate cleanly. |
| Local MITM proxy | Decrypt and repackage traffic | It makes HTTPS relayable without giving up inspection. |
| HTTP/2 tunnel | Multiplex many requests | It reduces overhead and smooths the traffic signature. |
What it beats, and what it does not
mhr-cfw is strongest when the problem is not raw speed but survivable access. It is more resilient than a single-provider proxy because its blocking surface is split. It is more specialized than broad privacy tools because it assumes you are already in a hostile network and willing to accept setup pain.
| Tool | Main strength | Main weakness |
|---|---|---|
| mhr-cfw | Two-hop disguise with latency optimization | Complex setup and dependence on two platforms |
| Cloudflare Worker-only relay | Simple deployment | Easy to target at one provider boundary |
| Google Apps Script-only relay | Useful fronting surface | Quotas and performance ceiling |
| Tor Meek | Mature censorship-evasion model | Heavier ecosystem and different use case |
| GoodbyeDPI | No remote infrastructure | Less effective against stronger filtering |
The tradeoff is the point
This is not casual infrastructure. It is a specialist response to networks that treat normal encrypted traffic as suspect. The project is compelling because it shows how far you can get by repurposing trusted SaaS surfaces and then spending real engineering effort to make that disguise fast enough to use.
That is the lesson here. Clever routing is easy to admire. Making it survive contact with a real user is the hard part.