We recently moved a production site between two Azure tenants. The plan was a zero-downtime cutover. That turned out to be impossible, and the reason is worth writing down — the documentation implies otherwise, and the failure mode is easy to walk into.
The plan that does not work
The standard trick for moving a custom domain between hosts is to pre-validate ownership on the new host before switching traffic. On Azure App Service you add a TXT record, the new site verifies you own the domain, and only then do you move the DNS record. The old site serves throughout.
Azure Static Web Apps rejects this:
Conflict — Request Envelope is invalid.
Please ensure the custom domain is not linked to another site.
A hostname can belong to exactly one Static Web App. There is no pre-validation, because validation requires claiming, and claiming requires the domain to be free. The domain must be released before it can be claimed, and that gap is the outage. Plan for one rather than trying to design it away.
Releasing a domain is not instant
az staticwebapp hostname delete returns immediately, with a warning that is
easy to skim past:
After deleting a custom domain, there can be a 15 minute delay for the change to propagate.
During that window, claiming the domain on the new app keeps returning
Conflict. Our first attempt retried five times over 75 seconds, gave up, and
then spent six minutes politely polling the status of a domain it had never
successfully added. That accounted for roughly half the total outage — entirely
self-inflicted.
Retry the claim across at least 20 minutes. Do not start polling status until a claim has actually been accepted. The distinction sounds pedantic; it is the difference between a four-minute outage and an eleven-minute one.
Both hostnames share one IP
This one is genuinely useful, and we only found it by checking.
dig +short old-app.azurestaticapps.net # 40.67.153.174
dig +short new-app.azurestaticapps.net # 40.67.153.174
Static Web Apps routes by Host header and hostname ownership, not by which
CNAME target a resolver happens to return. So moving the CNAME does not on its
own cause an outage — the old app keeps serving until the hostname is released.
That means DNS can be moved well ahead of the cutover, and by the time you release the domain, propagation is already finished. Sequence it the other way around and you add half an hour of DNS wait to your outage window.
DNS providers disagree with themselves
Before cutting over we sampled the authoritative nameservers repeatedly, and got this:
50% → 60% → 60% → 50% → 80% → 70% → 90% → 80% → 50%
Both authoritative servers for the zone, queried in a loop, disagreeing with
each other about a record we had already changed. Anycast fleets take time to
converge internally, and a single dig will happily tell you whichever answer
the node you hit happens to hold.
The practical rule: one query proves nothing. Sample repeatedly, and expect full agreement only once the previous record's TTL has lapsed. Ours went to 10/10 the moment the old 1800-second TTL expired — almost to the second.
Which suggests the obvious preparation: drop the TTL to 300 seconds a day before you plan to touch anything. It costs nothing and it buys back the entire propagation wait, both on the way out and on the way back if you need to roll back.
The sequence that worked
- Create the new app in the target tenant, deploy, and verify it fully on its
*.azurestaticapps.nethostname. Invisible to visitors. - Point the CNAME at the new host. Still no outage — see above.
- Wait for authoritative DNS to actually agree, sampling both nameservers.
- Release the hostname from the old app. The outage starts here.
- Claim it on the new app, retrying through the Conflict window for up to 20 minutes. Then wait for validation and the certificate — about seven minutes.
- Verify routes, TLS and headers against the live domain.
Keep the old app until you have confirmed the new one. It is not a fast rollback — re-adding the hostname hits the same 15-minute delay in reverse — but it is the only rollback you have, and deleting it is irreversible.