Background
cravatar.com is an avatar service handling over 20 million daily API calls. Its origin server is located in Nanjing (140.210.20.196), resulting in high latency for overseas users. Goal: route overseas traffic through Cloudflare CDN for acceleration, while leaving domestic traffic unchanged.
Sounds simple, right? Just use Alibaba Cloud DNS-based geo-routing + Cloudflare for SaaS — textbook configuration. Yet this seemingly “simple” requirement led me to fall into six separate pitfalls and iterate through six versions of a Cloudflare Worker before finally solving it.
Round One: Cloudflare for SaaS + custom_origin_server (Error 525)
Standard approach: add cravatar.com as a custom hostname under the wpcy.net zone; set custom_origin_server to cravatar.wpcy.net (a gray-clouded A record pointing to the origin).
Result: Error 525 — SSL handshake failed.
Investigation revealed that Cloudflare sends SNI=cravatar.wpcy.net when connecting to the origin, but the origin server rejects that SNI. We asked DevOps to update the Nginx config to include server_name cravatar.wpcy.net.
Local Nginx testing passed, yet external access still resulted in TCP resets. Ultimately we discovered: this wasn’t an Nginx issue — the data center’s network layer enforces an SNI allowlist, permitting only registered (ICP-filing-compliant) domains. Since cravatar.wpcy.net wasn’t registered, it was dropped at the network layer.
Could we use the custom_origin_sni parameter to force Cloudflare to send SNI=cravatar.com during origin connection? Sorry — this is an Enterprise-only feature.
Lesson: SNI filtering in Chinese data centers operates at a far lower network layer than expected — even correct Nginx configuration won’t help.
Round Two: Cloudflare Worker + resolveOverride over HTTPS (Still 525)
Since Cloudflare for SaaS’ origin settings couldn’t solve the SNI issue, we tried full control via a Worker.
Idea: The Worker fetches https://cravatar.com/path. Because the URL’s hostname is cravatar.com, the TLS SNI becomes cravatar.com (which passes the allowlist); resolveOverride points to the gray-clouded DNS record to resolve the origin IP (avoiding DNS loops).
Result: Still 525.
Cause: Cloudflare Workers’ resolveOverride changes DNS resolution, but Cloudflare’s internal TLS layer uses the resolveOverride hostname (cravatar.wpcy.net) — not the URL’s hostname — for SNI.
This behavior contradicts official documentation (which states SNI follows the URL hostname), yet real-world testing confirms it — likely a special behavior specific to Cloudflare for SaaS setups.
Lesson: In Cloudflare for SaaS contexts,
resolveOverridemay cause unexpected SNI behavior, differing from standard zones.
Round Three: Worker Fetches Origin IP Directly (403)
If domain-based routing fails, can we just fetch the origin by its IP?
fetch('http://140.210.20.196/avatar/test', { headers: { Host: 'cravatar.com' } })
Result: 403 — “Direct IP access not allowed.”
Cloudflare forbids Workers from fetching raw IPs. This is a hard security policy with no known bypass.
Lesson: Cloudflare Workers’
fetch()must use domain names — IPs are strictly prohibited.
Round Four: Worker HTTP Origin Fetch + cravatar.wpcy.net (404 / Data Center Block)
Let’s try HTTP (port 80) to bypass TLS/SNI entirely: fetch('http://cravatar.wpcy.net/path'), setting Host: cravatar.com in headers.
Result: 404, returning the data center’s interception page: “This website is currently inaccessible.”
Cause: The data center filters both TLS SNI and HTTP Host headers — and crucially, checks the target domain resolved from the URL, not the Host header. Though we sent Host: cravatar.com, the request’s destination domain (from DNS resolution) was cravatar.wpcy.net, triggering immediate network-layer blocking.
Lesson: Nanjing’s data center performs domain filtering across all protocols (HTTP & HTTPS), checking the DNS-resolved target domain — not the
Hostheader.
Round Five: Worker HTTP Origin Fetch + cravatar.com Hostname (301 Loop)
Try fetch('http://cravatar.com/path') (with Host: cravatar.com, passing data center checks), using resolveOverride to resolve the gray-clouded DNS record to the origin IP.
Result: 301 redirect to https://cravatar.com/path.
Cause: The wpcy.net zone has Always Use HTTPS enabled, and Cloudflare applies this rule even to Worker subrequests. So: Worker issues HTTP fetch → Cloudflare internally redirects to HTTPS → request returns to Worker → infinite loop.
Lesson: Cloudflare’s “Always Use HTTPS” setting applies to Worker subrequests — even when
resolveOverrideis used.
Round Six: Disable Always Use HTTPS + HTTP Origin Fetch (Finally 200!)
Disable “Always Use HTTPS” on the wpcy.net zone, then have the Worker fetch http://cravatar.com/path with resolveOverride.
Result: 200! Avatars delivered correctly!
Other custom hostnames (wpcy.com, wpcommunity.com) handle their own HTTPS redirects (WordPress and Discourse both enforce HTTPS natively), so they don’t rely on Cloudflare’s zone-level “Always Use HTTPS”.
Final Architecture
Overseas users → HTTPS → Cloudflare Anycast → Worker intercept
→ HTTP fetch to cravatar.com (resolveOverride → gray-clouded DNS → origin IP)
→ Origin port 80 (Host=cravatar.com — passes ICP filing check)
→ Response relayed back → cached at Cloudflare edge (/avatar/*, 30 days) → user
Domestic users → HTTPS → Alibaba Cloud DNS (mainland China routing) → origin port 443 (direct)
The final Worker code is just 55 lines — but writing those 55 lines required six iterations.
Key Lessons Learned
- Chinese data centers (at least this one in Nanjing) inspect both TLS SNI and HTTP
Host/target domain, allowing only ICP-registered domains. custom_origin_sniin Cloudflare for SaaS is an Enterprise-only feature.- In Cloudflare for SaaS contexts,
resolveOverridemay cause unexpected SNI behavior. - Cloudflare Workers cannot fetch raw IPs — domain names are mandatory.
- Cloudflare’s “Always Use HTTPS” applies to Worker subrequests, even with
resolveOverride. - Final solution: HTTP origin fetch +
resolveOverride+ disabling zone-level “Always Use HTTPS”.
None of these pitfalls were documented — every insight came from hands-on testing. Hope this saves future engineers some pain.