Background
cravatar.com is an avatar service with 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: Accelerate overseas traffic via Cloudflare CDN, 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 distinct pitfalls and iterate through six versions of a Cloudflare Worker before finally achieving success.
Round One: Cloudflare for SaaS + custom_origin_server (Error 525)
Standard approach: Add cravatar.com as a custom hostname under the wpcy.net zone, and set custom_origin_server to cravatar.wpcy.net (a gray-clouded A record pointing to the origin IP).
Result: Error 525 (“SSL handshake failed”).
Investigation revealed that Cloudflare sent the SNI cravatar.wpcy.net during origin requests, but the origin server rejected it. We asked the Fedora DevOps team to update the nginx configuration to include server_name cravatar.wpcy.net.
Nginx worked correctly in local tests, yet external access still resulted in TCP resets. Ultimately, we discovered: The issue wasn’t nginx — it was the datacenter’s network-layer SNI filtering, which only permits pre-registered (ICP-filing-compliant) domains. Since cravatar.wpcy.net lacked ICP filing, it was blocked at the network layer.
Could we use the custom_origin_sni parameter to force Cloudflare to send cravatar.com as the SNI during origin requests? Sorry — this is an Enterprise-only feature.
Lesson learned: SNI filtering in Chinese datacenters operates at a far lower (deeper) network layer than expected — even correct nginx configurations won’t help.
Round Two: Cloudflare Worker + resolveOverride over HTTPS (Still Error 525)
Since Cloudflare for SaaS origin settings couldn’t resolve the SNI issue, we tried full control via a Worker.
Idea: The Worker fetches https://cravatar.com/path. Because the URL hostname is cravatar.com, the TLS SNI becomes cravatar.com (which passes the whitelist). Meanwhile, resolveOverride points to the gray-clouded DNS record to obtain the origin IP (avoiding DNS loopback).
Result: Still error 525.
Reason: resolveOverride changes the resolved IP address, but Cloudflare’s internal TLS layer uses the hostname from resolveOverride (cravatar.wpcy.net) — not the URL hostname — for SNI.
This behavior contradicts official documentation (which states SNI follows the URL hostname), yet real-world testing confirmed it. Likely a special behavior specific to Cloudflare for SaaS.
Lesson learned: In Cloudflare for SaaS contexts,
resolveOverridemay behave differently regarding SNI compared to standard zones.
Round Three: Worker Fetching Directly by IP (Error 403)
If domain names cause trouble, what about fetching directly by IP?
fetch('http://140.210.20.196/avatar/test', { headers: { Host: 'cravatar.com' } })
Result: Error 403 (“Direct IP access not allowed”).
Cloudflare Workers prohibit fetching by raw IP addresses. This is a hard security policy — no bypass exists.
Lesson learned: Cloudflare Worker
fetch()must use domain names; IPs are strictly forbidden.
Round Four: HTTP Origin Fetch + cravatar.wpcy.net (Error 404 / Datacenter Blocking)
Let’s try HTTP (port 80) to bypass TLS/SNI entirely: fetch('http://cravatar.wpcy.net/path'), setting Host: cravatar.com.
Result: Error 404, with response body showing the datacenter’s interception page: “This website is temporarily inaccessible.”
Cause: The datacenter filters not only TLS SNI, but also the HTTP Host header — and more critically, the target domain name parsed from the request URL. Although we set Host: cravatar.com, the request URL itself is cravatar.wpcy.net, so the datacenter sees cravatar.wpcy.net at the network layer and blocks it outright.
Lesson learned: The Nanjing datacenter enforces domain filtering across all protocols (HTTP & HTTPS), checking the DNS-resolved target domain — not just the
Hostheader.
Round Five: HTTP Origin Fetch + cravatar.com Hostname (301 Redirect Loop)
Try fetch('http://cravatar.com/path') (with Host: cravatar.com, passing datacenter checks), using resolveOverride to get 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 → Cloudflare internally redirects 301 → HTTPS → back to Worker → infinite loop.
Lesson learned: Cloudflare’s “Always Use HTTPS” setting applies to Worker subrequests too — even when
resolveOverrideis used.
Round Six: Disable “Always Use HTTPS” + HTTP Origin Fetch (Finally, HTTP 200!)
Disable “Always Use HTTPS” on the wpcy.net zone, then have the Worker fetch http://cravatar.com/path with resolveOverride.
Result: HTTP 200! Avatars delivered correctly!
Other custom hostnames (e.g., wpcy.com, wpcommunity.com) handle HTTPS redirection independently — WordPress and Discourse each enforce HTTPS at the application level, eliminating reliance on Cloudflare zone-level settings.
Final Architecture
Overseas users → HTTPS → Cloudflare Anycast → Worker intercepts
→ HTTP fetch to cravatar.com (resolveOverride → gray-clouded DNS → origin IP)
→ Origin port 80 (Host=cravatar.com, passes ICP-filing check)
→ Response returned → cached at Cloudflare edge (30-day TTL for /avatar/*) → user
Domestic users → HTTPS → Alibaba Cloud DNS (geo-routed within China) → origin port 443 (direct connection)
The final Worker code is only 55 lines — but writing those 55 lines required six full iterations.
Key Lessons Summarized
- Chinese datacenters (at least this Nanjing facility) inspect both TLS SNI and HTTP
Host/target domain — only pre-registered (ICP-filing-compliant) domains are permitted. custom_origin_sniin Cloudflare for SaaS is an Enterprise-only feature.- In Cloudflare for SaaS contexts,
resolveOverridemay exhibit unexpected SNI behavior. - Cloudflare Workers cannot fetch resources by raw IP address.
- Cloudflare’s “Always Use HTTPS” setting applies to Worker subrequests — even with
resolveOverride. - Final working solution: HTTP origin fetch +
resolveOverride+ disabling zone-level “Always Use HTTPS”.
None of these pitfalls are documented — all were uncovered through hands-on testing. Hope this saves future engineers some pain.