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.”
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.