Step 5 of 6

Redirects: proxy vs. 301.

Not every path needs to be proxied. Some should permanently move. A reverse proxy is also the right place to enforce HTTPS and the canonical host.

When to redirect instead of proxy

The right status code

Pattern: redirect rule in front of proxy rules

const RULES = [
  // Force HTTPS on canonical host
  { test: (url) => url.protocol === 'http:', redirect: (url) => `https://${url.host}${url.pathname}${url.search}` },

  // Consolidate apex onto www
  { test: (url) => url.hostname === 'example.com', redirect: (url) => `https://www.example.com${url.pathname}${url.search}` },

  // Permanent path move
  { test: (url) => url.pathname.startsWith('/old-blog/'), redirect: (url) => `https://www.example.com/blog${url.pathname.slice(9)}${url.search}` },

  // then proxy rules…
];

for (const r of RULES) {
  if (r.test && r.test(url)) {
    return Response.redirect(r.redirect(url), 301);
  }
}

Try it: mix redirects and proxy rules

Edit the config, then enter a request URL to see what the Worker would do. Nothing is sent over the network — this is a faithful reimplementation of the same rules in the browser.

Path rules (first match wins)

Gotchas

Redirect chains hurt SEO. If example.com/old-blog/x → www.example.com/old-blog/x → www.example.com/blog/x, that's two hops. Collapse them into one rule that goes straight to the final URL.
Don't proxy a redirect. If the upstream itself returns a 301 with its origin host in the Location, rewrite that header before passing it back: upstream.headers.set('location', loc.replace(origin, canonical)).