Article

Deploying an Astro website to Cloudflare Workers

12 October 2026 ยท Francisco Gaspar

This site is a static Astro build. The Worker in front of it does one job: send the old URLs to the current ones.

Astro is configured with output: 'static' and site: 'https://franciscogaspar.xyz'. Deploy is astro build && wrangler deploy. The build writes dist/. Wrangler serves that directory as assets and runs the Worker before the asset lookup.

{
  "main": "worker/index.ts",
  "assets": {
    "directory": "./dist",
    "binding": "ASSETS",
    "run_worker_first": true
  }
}

run_worker_first is the part that is easy to miss. Without it, Cloudflare serves a file when one exists and the Worker never sees the request. The old /cv paths have to hit the Worker so it can redirect.

const legacyPath = url.pathname === "/cv" || url.pathname.startsWith("/cv/");
if (url.hostname === "cv.franciscogaspar.xyz" || legacyPath) {
  if (legacyPath) url.pathname = url.pathname.slice("/cv".length) || "/";
  url.hostname = "franciscogaspar.xyz";
  return Response.redirect(url.toString(), 301);
}

return env.ASSETS.fetch(request);

cv.franciscogaspar.xyz and any path under /cv return a 301 to the same path on franciscogaspar.xyz. Everything else is env.ASSETS.fetch(request), the Astro build. Wrangler routes attach the Worker to franciscogaspar.xyz/* and to the old subdomain.

There is no server render. The pages are HTML files. The Worker is the redirect, and that is all it should be. Page views are a separate script, Umami on its own host.

Experience and projects