A friend pinged me. “Your site doesn’t load.”

I opened it on my laptop. It loaded fine. I asked which URL they typed. They had typed www.abhiigatty.com.

I had never typed that. Always the bare domain. Always abhiigatty.com.

I opened a terminal.

$ dig www.abhiigatty.com
;; ANSWER SECTION:
(nothing)

That is the whole story in one line. The www subdomain had no DNS record at all. Not an A record, not a CNAME, nothing. Browsers cannot route a request without a name to resolve, so every visitor who guessed www. was hitting a wall.

How most people register a domain

When you buy a domain and point it at a host, you usually configure two things: the apex (abhiigatty.com) and the www subdomain (www.abhiigatty.com). Most setup wizards do both for you. They have for years.

I had not used a wizard. I bought the domain, added one DNS record for the apex pointing at Cloudflare, deployed the site, opened it in my browser, and called it done. Everything worked, so I never went looking for what I had skipped.

The split between “the URL I always type” and “the URL someone else might type” is invisible until somebody else types it. The redirect from www. to apex is something every other site has, so most people assume yours does too.

The fix is two parts, not one

Step one is to make www.abhiigatty.com resolve at all. Step two is to decide what should happen when it does.

For step one, Cloudflare gives you a one-click answer if your site is on a Worker. Workers and Pages → your worker → Settings → Domains and Routes → Add Custom Domain → type www.abhiigatty.com. Cloudflare provisions the DNS record and the SSL certificate in about thirty seconds.

I went the in-code route instead, because I want every piece of infrastructure to live in the repo. In wrangler.jsonc:

"routes": [
  { "pattern": "abhiigatty.com",     "custom_domain": true },
  { "pattern": "www.abhiigatty.com", "custom_domain": true }
]

You have to list both. Wrangler treats routes as the full set of bindings for the worker. If you list only www., it will quietly unbind the apex on the next deploy, which is the worst possible failure mode for this kind of change.

Step two is the redirect. The convention is to pick one canonical hostname and 301 the other to it, so search engines and links don’t see two copies of the same site. I picked the apex as canonical, which matches what was already working.

I added an Astro middleware:

// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';

export const onRequest = defineMiddleware((context, next) => {
  const url = new URL(context.request.url);
  if (url.host === 'www.abhiigatty.com') {
    url.host = 'abhiigatty.com';
    return Response.redirect(url.toString(), 301);
  }
  return next();
});

Merged the PR. Watched the deploy. Curled www.abhiigatty.com. Got back HTTP 200 with the homepage HTML, not the 301 I expected.

The Workers gotcha

This is the part I did not know.

When you bind static assets to a Worker using the assets field in wrangler.jsonc, Cloudflare’s runtime serves matching files directly from the asset binding, before your Worker code ever runs. For a static site, that is most of your URLs. The Worker handler only fires for paths that do not match a built file.

My middleware lived in the Worker handler. So / on www.abhiigatty.com matched a built index.html in the asset binding, got served straight from the edge, and my redirect was never invoked.

The fix is one line in wrangler.jsonc:

"assets": {
  "directory": "./dist",
  "binding": "ASSETS",
  "run_worker_first": true
}

With run_worker_first: true, the Worker handler runs for every request. The Astro adapter falls through to the asset binding for any path the route handler does not own, so static assets still get served from the asset binding. The middleware now runs first, sees the www. host, and redirects.

What I would tell past me

A few small things, all the kind that only become obvious in hindsight.

When you set up a domain, type www. once and confirm it works, even if you never plan to share that URL. The cost of the check is fifteen seconds. The cost of finding out from a friend is an unknown number of visitors who never told you.

If your hosting platform serves static assets through a separate binding, read the docs on how middleware interacts with that binding before you write middleware. The asset binding being “in front of” the Worker is the kind of detail that is obvious to people who already know it and invisible to everyone else.

And the smaller lesson, which is really a writing lesson. The reason this post exists is that I learned two specific Cloudflare facts that I would not have gone looking for. Most of the things I know about my own infrastructure, I learned the same way: somebody hit a thing that did not work, I went to find out why, and in the process I picked up a piece of the platform I had been quietly using for months without understanding.