Deploy the Worker
With routing, rewrites, headers, and redirects in place, deploying is mostly
configuration: a wrangler.toml, a custom domain, and a robots.txt strategy.
Scaffold the Worker
Use the wrangler-init script you set up — it loads Node 24 via nvm and runs wrangler init:
wrangler-init my-reverse-proxy
# Pick: "Hello World" Worker → JavaScript → no git → no deploy yet. wrangler.toml
name = "reverse-proxy"
main = "src/index.js"
compatibility_date = "2025-05-01"
# Bind the Worker to your canonical host. The zone must be on Cloudflare.
[[routes]]
pattern = "www.example.com/*"
zone_name = "example.com"
# Optional: a workers.dev URL for staging.
workers_dev = true The full Worker, in one file
// src/index.js
const CANONICAL = 'www.example.com';
const PRIMARY = 'brand.webflow.io';
const SECONDARY = 'legacy.example.com';
const RULES = [
{ prefix: '/blog', to: SECONDARY },
{ prefix: '/shop', to: SECONDARY },
{ prefix: '/', to: PRIMARY },
];
export default {
async fetch(request) {
const url = new URL(request.url);
// 1. Enforce canonical scheme + host
if (url.protocol === 'http:') {
url.protocol = 'https:';
return Response.redirect(url.toString(), 301);
}
if (url.hostname !== CANONICAL) {
url.hostname = CANONICAL;
return Response.redirect(url.toString(), 301);
}
// 2. Pick origin by path
const rule = RULES.find(r =>
url.pathname === r.prefix ||
url.pathname.startsWith(r.prefix === '/' ? '/' : r.prefix + '/')
);
if (!rule) return new Response('Not Found', { status: 404 });
// 3. Fetch upstream
const upstreamUrl = `https://${rule.to}${url.pathname}${url.search}`;
const upstream = await fetch(upstreamUrl, request);
// 4. Rewrite canonical/og:url/hreflang back to the canonical host
return new HTMLRewriter()
.on('link[rel="canonical"], link[rel="alternate"]', {
element(el) {
const href = el.getAttribute('href');
if (href) el.setAttribute('href', href.replace(rule.to, CANONICAL));
},
})
.on('meta[property^="og:"]', {
element(el) {
const v = el.getAttribute('content');
if (v && v.includes(rule.to)) el.setAttribute('content', v.replace(rule.to, CANONICAL));
},
})
.transform(upstream);
},
}; Custom domain
- In the Cloudflare dashboard, open the Worker → Triggers → Custom Domains.
- Add
www.example.com. Cloudflare creates the DNS record automatically. - Remove any existing A/CNAME records for
www.example.comthat point at an origin — the Worker now owns that hostname.
robots.txt
Decide where robots.txt and sitemap.xml come from. Usually the primary
origin's. Add an explicit rule so neither path is rewritten through the secondary:
// before the path rules
if (url.pathname === '/robots.txt' || url.pathname === '/sitemap.xml') {
return fetch(`https://${PRIMARY}${url.pathname}`);
} Ship it
npx wrangler deploy
# tail logs from production:
npx wrangler tail
That's the whole proxy. Iterate by adding rules, watching
wrangler tail,
and checking that view-source: on your canonical host never shows an origin URL.
Where to go next
- Add unit tests with @cloudflare/vitest-pool-workers.
- Cache aggressively with the
cf.cacheEverythingfetch option. - Use
HTMLRewriterstreaming for very large pages. - Move to KV or D1 for redirect maps that change frequently.