Redirect The Apex Domain To www In ASP.NET Core

I had both https://resolv.it.com/ and https://www.resolv.it.com/ pointing at the same Azure Container App, so the site worked on both but browsers saw two separate sites. This is how I made www the one true host, in code, without touching UAT.

Two things go wrong when both hosts serve the site:

  • Cookies and saved passwords are per host, so a user who signs in on one host is signed out on the other.
  • Search engines see two copies of the same site and rank each lower.

1. Pick one host

Either is fine, the point is picking one. I went with www because it works with a CNAME (the host can move without a DNS change) while the apex needs a fixed A record. The DNS side is in Azure Container App With External DNS.

2. Add the redirect in Program.cs

1
2
3
4
5
6
7
using Microsoft.AspNetCore.Rewrite;

// Redirect only the apex, anything else (www, UAT, localhost) is left alone
app.UseRewriter(new RewriteOptions().AddRedirectToWwwPermanent("resolv.it.com"));

app.UseHttpsRedirection();
app.UseRouting();

AddRedirectToWwwPermanent ships with the framework (Microsoft.AspNetCore.Rewrite), so there is no package to add. The domain you pass is the important bit: without it every host without a www. prefix would be redirected, including my UAT *.azurecontainerapps.io host. With it, only resolv.it.com is redirected.

I read the domain from config (Environments:ProdHost is www.resolv.it.com, so I strip the www.), but a hard-coded <your-apex-domain> works just as well.

Put it before UseHttpsRedirection() so http://resolv.it.com is sorted out in the same pipeline.

Docs: URL Rewriting Middleware

3. Test it

You can fake the host with curl, no DNS needed:

1
2
curl -i -H "Host: resolv.it.com" "http://localhost:5000/a/b?x=1"
curl -i -H "Host: <your-uat-host>" "http://localhost:5000/"

The first returns 308 Permanent Redirect with Location: http://www.resolv.it.com/a/b?x=1, so the path and query string survive. The second returns 200, so UAT is untouched.

Things to know

  • The status is 308, not 301. Both are permanent and search engines treat them the same, 308 just keeps the HTTP method (a POST stays a POST).
  • The redirect keeps the scheme of the request, so plain http://resolv.it.com takes two hops (to http://www... then up to https://www... via the HTTPS redirect). Only http requests to the apex pay for this.
  • It only works if the app sees the original Host header. Azure Container Apps ingress passes it through, check with curl -I https://resolv.it.com/ after deploying.
  • If you would rather not do this in the app, the standard place is the edge (Front Door, Cloudflare, your registrar’s forwarding). I could not get the GoDaddy redirect working, which is why this lives in the app.