Skip to content
Go back

Netlify Builds It, Cloudflare Serves It: The Nameserver Swap That Broke Nothing 🛰️

(updated )

There is one field in your DNS that looks like it should take your site offline. 🛰️

Everything else feels reversible. Add a record, delete a record, change an IP — at worst you’re wrong for a TTL and then you’re not. The nameserver field doesn’t feel like that. It reads like you’re handing the entire domain to a different company, because you are.

I moved mine last week. Netlify still builds and deploys every push; Cloudflare now sits in front and serves the traffic. Two companies, split down the middle, joined at the one setting I’d been avoiding.

It didn’t drop a single request. And that turned out to be the least interesting thing about the whole job.

Before touching anything, I counted what was there 🧮

The reason I’d put it off wasn’t technical. It was that I didn’t know what I’d break.

So I enumerated first. Four query types against both the apex and www, straight from the authoritative server:

A      @     54.253.x.x
A      www   54.253.x.x
AAAA   @     2406:da1c:cc9:xxxx::xxx
AAAA   www   2406:da1c:cc9:xxxx::xxx
TXT    @     "google-site-verification=…"

Five records. No MX. No CAA. No SRV.

That last paragraph is the whole risk assessment. No MX means no email on this domain, which means no way to silently break someone’s inbox — the failure mode that makes DNS migrations genuinely frightening. What I had instead was two address records, one v6 pair, and a verification string Google checks occasionally.

Not a migration. A copy-paste.

Why both sets of nameservers answer at once 🍰

Here’s the part I’d misunderstood for years: changing your nameserver isn’t a swap. It’s an overlap.

When the registry publishes the new delegation, resolvers don’t all notice at the same time. They notice when their cached copy expires — and the registry told me exactly how long that was:

ownyourstack.dev.  10800  IN  NS  maria.ns.cloudflare.com.
ownyourstack.dev.  10800  IN  NS  hasslo.ns.cloudflare.com.
                              ^^^^ TTL, in seconds

10800 is three hours. For up to three hours after I flipped the switch, some resolvers were still asking the old nameservers. Those old nameservers were still running, still holding the old zone, still handing out valid answers.

Because I’d mirrored the records rather than moving them, there was no moment where the answer was “nothing.”

Diagram of the nameserver handover. A resolver asking for ownyourstack.dev forks into two paths. The upper path goes to the old Netlify DNS nameservers (dns1 to dns4 on nsone.net), which return the origin address 54.253.x.x straight to Netlify. The lower path goes to the new Cloudflare nameservers (hasslo and maria), which return the proxy address 104.21.x.x at the Cloudflare edge. Both paths converge on a green 200 OK box, because Cloudflare fronts Netlify either way. A strip along the bottom records the registry NS TTL of 10800 seconds, three hours, noting that the old answers stay valid until they expire so no query ever goes unanswered.

The two paths resolve to different addresses, and it doesn’t matter: the old one goes straight to Netlify, the new one goes to Cloudflare, which proxies straight to Netlify. Different roads, same house.

What I actually watched 📡

I kept querying the registry and the public resolvers side by side through the window, because I wanted to see the handover rather than take it on faith.

The registry flipped almost immediately. My own resolver, Google’s, and Cloudflare’s still reported the old nameservers — cached, exactly as advertised. Then, one by one, they turned.

Throughout, both sets were answering. I never once got a referral into the void.

The zone itself sat at pending for a while longer while Cloudflare confirmed the delegation, then went active. At that point the proxied records stopped returning the origin address and started returning Cloudflare’s:

A  ownyourstack.dev  →  104.21.x.x
                       172.67.x.x
server: cloudflare
cf-ray: a45ceac3dd44f0cd-MEL

Build still Netlify. Serve now Cloudflare. Same push, same deploy, different front door.

The boring part was the actual work 🔧

Here’s what I didn’t expect. The DNS migration — the thing I’d been putting off for months — took about ten minutes and needed no courage at all.

The thing that actually needed fixing was a cache header.

Once traffic was flowing through Cloudflare I started reading the response headers, and found this on nearly everything:

cache-control: public, max-age=0, must-revalidate
cf-cache-status: DYNAMIC

That’s Netlify’s default, and it’s the correct default: never cache HTML, always recheck. But it was applying to everything, including the static assets Astro had already fingerprinted into unique filenames. Every single request for a stylesheet was making a round trip to confirm the file hadn’t changed — when the filename itself guaranteed it hadn’t.

Cloudflare was honouring the instruction faithfully. It was just the wrong instruction for 27 of the files on the site.

So I split the rules in a _headers file:

/*
  Cache-Control: public, max-age=0, must-revalidate

/_astro/*
  Cache-Control: public, max-age=31536000, immutable

Same reasoning as the nameservers, pointed the other way. HTML can change the instant I publish, so it keeps revalidating — it stays DYNAMIC and that’s correct. But Footer.COFj3KgE.css cannot become a different file under the same name, so it can be cached hard for a year. If the content changes, Astro emits a new hash and every browser fetches a URL it has never seen.

The headers went from REVALIDATED to HIT.

$ curl -sI https://ownyourstack.dev/_astro/Footer.COFj3KgE.css | grep -E 'cache-control|cf-cache-status'
cache-control: public, max-age=31536000, immutable
cf-cache-status: HIT

What I’d tell past-me 🪞

The nameserver field isn’t dangerous because it’s powerful. It’s dangerous because it looks powerful while doing the least surprising thing in the whole system — and the real risk had been sitting in a header file the entire time, doing nothing dramatic at all.

Mirrored records, an overlap window the registry had already told me the length of, and a build pipeline that never noticed any of it.

If you’ve been avoiding the same swap because it feels like the big red button: count your records first. If there’s no MX, you’re mostly copying.


I also turned on Cloudflare’s Crawler Hints while I was in there, which quietly takes care of IndexNow for me — one toggle, no key file, no ping script to maintain. Small win, but it’s the kind of thing worth doing while the dashboard is already open. 😉

You’ve watched me go from a Windows machine I hated through a Linux box I actually own, and now apparently into caring about nameserver TTLs. Is owning your own edge the natural end of that, or have I just found a more elaborate way to avoid doing something else? 👇


Editor’s note, October 2026: the split described above is gone. Netlify no longer builds here either — the build moved to Cloudflare, so there is one vendor in front and behind instead of two. I wrote up why that happened.


Share this post on:

Previous Post
I Built A Pipeline To Survive My Job Hunt — Then I Realised It Was The Product 💡
Next Post
Nobody Tells You Your Link Preview Is Broken: My og:image 404'd The Whole Time 🙈