Resources / Field guide

Wiring a custom domain to Azure App Service

The DNS part is mechanical. The part nobody warns you about is the HTTP 400 your app starts returning the moment the new hostname goes live — from a setting that lives in your code, not the Azure portal. Here's the whole path, end to end.

Pointing a custom domain at an Azure App Service is one of those tasks that's ninety percent mechanical and ten percent "why is the site suddenly returning 400 to everyone." The DNS part is well-trodden. The part nobody warns you about is what happens inside your app the moment the new hostname starts working. Here's the whole path, end to end, including the gotcha that turns a finished setup into a broken one.

The DNS records Azure actually wants

In the portal, go to your App Service → Custom domains → + Add custom domain. Azure asks for the hostname and then shows you the exact DNS records it expects. There are two cases, and the difference matters.

A subdomain like www is a CNAME. It points at your app's default azurewebsites.net hostname. Straightforward.

The apex (root) domain — the bare example.com with no www — is where people get tripped up. You cannot use a CNAME on a root domain; the DNS spec doesn't allow it (a root has other records, like MX and SOA, that a CNAME would conflict with). So the apex needs an A record pointing at the App Service's inbound IP address, which Azure shows you on that same screen.

Alongside the A record, Azure wants a TXT record for domain verification, with the host set to asuid and a value Azure generates. That's how Azure proves you actually control the domain before it'll serve traffic for it.

So for a typical setup you're creating:

www    CNAME  your-app.azurewebsites.net
@      A      <App Service inbound IP that Azure shows you>
asuid  TXT    <verification token that Azure shows you>

Creating them at your DNS provider

This is the same idea wherever your DNS lives, but a few things bite regardless of provider:

If the domain was previously parked or redirected, there's often an existing record or a redirect on @ pointing at old infrastructure. Deactivate the redirect and clear the stale record first — until you do, you may not even get an editable A record row. Then set the apex A record to the IP Azure gave you, and drop the TTL low (five minutes) while you work, so changes propagate fast instead of making you wait out an hour-long cache.

Leave your MX and mail records alone. Those are email and have nothing to do with web hosting; people sometimes "clean up" the zone and knock their own mail offline. Touch only the records Azure asked for.

Back in Azure, click Validate. It checks for both the A record and the asuid TXT. DNS takes a couple of minutes, so re-click until both go green — the "request error" notices before propagation are harmless. Then Add.

HTTPS, the easy part

Azure will provision a free App Service Managed Certificate over SNI SSL for the domain. If it shows "No binding" after adding, use Add binding → App Service Managed Certificate, then refresh until the domain reads Secured. Set HTTPS Only = On in the app's configuration so plain http:// auto-upgrades. This part usually just works.

The gotcha: HTTP 400 the moment the domain goes live

Here's the one that'll have you staring at a working DNS setup wondering what you broke. Your domain validates, the cert issues, everything's green — and the site returns HTTP 400 to every request on the new hostname.

The cause is in your app, not Azure. ASP.NET Core has a host filtering feature driven by the AllowedHosts setting in appsettings.json. It exists to reject requests whose Host header doesn't match a host you expect — a defense against host-header injection. The default value is "*", which allows everything. But if AllowedHosts was ever locked down to specific hostnames (a common hardening step, sometimes added by a security-scan remediation), then the instant your new domain starts sending requests, ASP.NET Core sees a Host header it doesn't recognize and rejects it with a 400 — before your code runs.

The fix is to make sure the new hostnames are allowed. Either keep the permissive default:

{
  "AllowedHosts": "*"
}

or, if you're deliberately restricting it, list every hostname the app should answer to:

{
  "AllowedHosts": "example.com;www.example.com"
}

Use the semicolon-separated explicit list when you want the hardening; just remember it's now a thing you have to update every time you add a domain. That's the whole trap: the host-filtering setting and the custom-domain setup live in completely different places (one in your code, one in the Azure portal), so it's easy to finish the DNS side, declare victory, and not connect the 400 to a config value you set weeks ago.

Two smaller traps. Watch for a typo in the Azure domain field (example.xom for example.com) — Azure builds the asuid record it looks for from exactly what you typed, so a typo makes it validate against a verification record that doesn't exist. And give DNS a beat: most "it won't validate" problems are just propagation, and the low TTL you set earlier means you're waiting minutes, not hours.

The short version

Subdomain is a CNAME, apex is an A record plus an asuid TXT, certificate is App Service Managed over SNI SSL, and HTTPS Only on. Then, before you close the ticket, check AllowedHosts — because the last step of "add a custom domain" is making sure your app is actually willing to answer to it.

Shipping a .NET app to Azure?

Custom domains, certs, the 400s that only show up in the cloud — I set up and debug real ASP.NET / Azure deployments directly. If you'd rather not lose an afternoon to it, let's talk.

Work with me