Vercel delivers an excellent development and deployment experience, but the default Anycast IPs assigned to free/basic plan users have poor routing quality in mainland China, resulting in high latency, packet loss, or outright connection blocks.
The conventional workaround is routing your domain through Cloudflare's proxy (orange cloud), but this only addresses the blocking issue — the Anycast IPs allocated by CF's free tier still offer no connectivity guarantee within China. Attempting to directly modify CF DNS records — pointing your domain to a China-optimized CF node via an A or CNAME record — runs into the following technical barriers:
This solution uses Cloudflare for SaaS (Custom Hostnames) combined with Worker route mapping to bypass these network-layer restrictions, while also providing edge caching and centralized multi-site management via Workers.
Request flow:
User Request → Optimized CF Node → SaaS Fallback Origin → Worker Intercept → Rewrite Host → Vercel Origin (with cache) → Response
Required resources:
| Resource | Description | Example |
|---|---|---|
| Primary Domain | Your public-facing business domain | app.domain.com |
| Auxiliary Domain | Any CF-managed domain, serves as the routing hub | proxy.dev |
| Vercel Project | Keep only the system-generated .vercel.app domain |
your-project.vercel.app |
⚠️ Critical prerequisite: You must remove the primary domain binding from your Vercel project's Domains settings. Leaving it causes SNI hijacking that creates internal CF routing conflicts, rendering the entire setup non-functional.
The auxiliary domain acts as the internal traffic relay hub. First, configure a fallback origin by adding an A record to the auxiliary domain's (proxy.dev) DNS management page:
| Field | Value | Notes |
|---|---|---|
| Name | origin |
Full address: origin.proxy.dev |
| IPv4 Address | 192.0.2.1 |
RFC 5737 reserved IP, placeholder only — actual requests are intercepted by the Worker |
| Proxy Status | ✅ Proxied (orange cloud) | Must be enabled, otherwise the Worker cannot intercept |
The Worker's core responsibilities: intercept all requests to the fallback origin → look up the corresponding Vercel origin in the route map → rewrite the Host header and fetch from origin → enable edge caching via the cf parameter.
Create a Worker in the CF dashboard and write the following code:
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const requestHost = url.hostname;
// Domain map: external domains -> Vercel origin domains
const routeMap = {
"app.domain.com": "your-project.vercel.app",
"www.app.domain.com": "your-project.vercel.app",
"blog.domain.com": "your-project.vercel.app",
};
const targetHostname = routeMap[requestHost];
if (!targetHostname) {
return new Response("Forbidden: Domain not configured in routing map.", { status: 403 });
}
// Rewrite URL
url.hostname = targetHostname;
// Clone request, keep original method/headers
const newRequest = new Request(url.toString(), request);
newRequest.headers.set('Host', targetHostname);
const isGet = request.method === 'GET' || request.method === 'HEAD';
const isNextStatic = url.pathname.startsWith('/_next/static/') || url.pathname.startsWith('/_next/image');
const isPublicAsset = url.pathname.startsWith('/favicon') || url.pathname.startsWith('/robots.txt') || url.pathname.startsWith('/sitemap');
// Cache only static asset GETs; never cache HTML/POST/Server Actions
if (isGet && (isNextStatic || isPublicAsset)) {
return fetch(newRequest, {
cf: {
cacheTtl: 3600,
cacheEverything: true,
},
});
}
// Don't cache other requests to avoid stale actionId/HTML
return fetch(newRequest, {
cf: {
cacheEverything: false,
},
});
},
};After deploying, go to Worker Settings → Triggers → Routes and add the interception rule:
origin.proxy.dev/*proxy.dev)To allow CF edge nodes to accept traffic from the primary domain into the fallback origin, complete the SaaS authorization. Navigate to SSL/TLS → Custom Hostnames on your auxiliary domain:
origin.proxy.dev and wait for the status to show "Active".app.domain.com).Why not CNAME directly to the optimized domain? CF has validation logic restrictions for domains within the same account. Pointing the primary domain's CNAME directly to a third-party optimized domain will cause the SaaS custom hostname status to become invalid. An intermediate relay through the auxiliary domain (double-CNAME) is required.
① Add bridge record on auxiliary domain:
In proxy.dev's DNS, add a CNAME record:
cnd (full address: cnd.proxy.dev)cloudflare.182682.xyz)② Add traffic routing record on primary domain:
In your primary domain's DNS, set the business records (@ and www) to CNAME to cnd.proxy.dev:
⚠️ Easy-to-miss critical step! When a request carrying the primary domain Host enters the auxiliary domain's Zone, if that primary domain is not explicitly declared in the Worker's trigger routes, the Worker will not execute. Traffic then flows directly to the placeholder IP (
192.0.2.1) bound to the fallback origin, causing a connection timeout (Error 522).
In Worker Settings → Triggers → Routes, add a route for each business domain:
| Route | Zone |
|---|---|
app.domain.com/* |
Auxiliary domain (proxy.dev) |
www.app.domain.com/* |
Auxiliary domain (proxy.dev) |
blog.domain.com/* |
Auxiliary domain (proxy.dev) |
This architecture is designed as a centralized routing hub — the underlying network is built only once. For each new Vercel project going forward, simply repeat this four-step standard procedure:
| Step | Action | Location |
|---|---|---|
| 1. Update route map | Add "new-domain": "new-vercel-domain" to routeMap |
CF Worker code |
| 2. Add SaaS hostname | Register the new domain, complete TXT record verification | Auxiliary domain → Custom Hostnames |
| 3. Configure DNS (gray cloud) | New domain CNAME to cnd.proxy.dev (DNS only) |
Primary domain DNS provider |
| 4. Add Worker trigger | Add route new-domain/*, attach to auxiliary domain Zone |
CF Worker Triggers |
In this setup, the bridge CNAME (cnd.proxy.dev) points to a third-party-maintained optimized node pool — the only link in the chain outside your control. This introduces a single point of failure (SPOF) risk.
Cloudflare Worker Cron Watchdog: SPOF Fix and Automatic DNS Failover for the Vercel Proxy Architecture has now been updated to the final safer design: instead of recommending direct failover to Cloudflare official IPs, it uses a three-tier priority model built from an optional official-hostname layer, a custom CNAME pool, and a fallback IP pool, plus recovery debounce, optional KV-backed state persistence, and structured logs.