Dealing with Application Base URLs and Razor link generation while hosting ASP.NET web apps behind Reverse Proxies
I'm quietly moving my Website from a physical machine to a number of Cloud Services hosted in Azure. This is an attempt to not just modernize the system - no reason to change things just to change them - but to take advantage of a number of benefits that a straight web host sometimes doesn't have. I want to have multiple microsites (the main page, the podcast, the blog, etc) with regular backups, CI/CD pipeline (check in code, go straight to staging), production swaps, a global CDN for content, etc.
I'm also moving from an ASP.NET 4 (was ASP.NET 2 until recently) site to ASP.NET Core 2.x LTS and changing my URL structure. I am aiming to save money but I'm not doing this as a "spend basically nothing" project. Yes, I could convert my site to a static HTML generated blog using any number of great static site generators, or even a Headless CMS. Yes I could host it in Azure Storage fronted by a CMS, or even as a series of Azure Functions. But I have 17 years of content in DasBlog, I like DasBlog, and it's being actively updated to .NET Core and it's a fun app. I also have custom Razor sites in the form of my podcast site and they work great with a great workflow. I want to find a balance of cost effectiveness, features, ease of use, and reliability. What I have now is a sinking feeling like my site is gonna die tomorrow and I'm not ready to deal with it. So, there you go.
Currently my sites live on a real machine with real folders and it's fronted by IIS on a Windows Server. There's an app (an IIS Application, to be clear) leaving at \ so that means hanselman.com/ hits / which is likely c:\inetpub\wwwroot full stop.
For historical reasons, when you hit hanselman.com/blog/ you're hitting the /blog IIS Application which could be at d:\whatever but may be at c:\inetpub\wwwroot\blog or even at c:\blog. Who knows. The Application and ASP.NET within it knows that the site is at hanselman.com/blog.
That's important, since I may write a URL like ~/about when writing code. If I'm in the hanselman.com/blog app, then ~/about means hanselman.com/blog/about. If I write /about, that means hanselman.com/about. So the ~ is a shorthand for "starting at this App's base URL." This is great and useful and makes Link generation super easy, but it only works if your app knows what it's server-side base URL is.
To be clear, we are talking about the reality of the generated URL that's sent to and from the browser, not about any physical reality on the disk or server or app.
I've moved my world to three Azure App Services called hanselminutes, hanselman, and hanselmanblog. They have names like http://hanselman.azurewebsites.net for example.
ASIDE: You'll note that hitting hanselman.azurewebsites.com will hit an app that looks stopped. I don't want that site to serve traffic from there, I want it to be served from http://hanselman.com, right? Specifically only from Azure Front Door which I'll talk about in another post soon. So I'll use the Access Restrictions and Software Based Networking in Azure to deny all traffic to that site, except traffic from Azure - in this case, from the Azure Front Door Reverse Proxy I'll be using.
That looks like this in this Access Restrictions part of the Azure Portal.
app.Use((context, next) => {
var path = new PathString("/blog");
if (context.Request.Path.StartsWithSegments(path, out var remaining)) {
context.Request.PathBase = path;
context.Request.Path = remaining;
}
return next.Invoke();
});
Also, make sure you don't enable 'Url rewrite' in the routing rule(I've had it enabled when first designing the Front Door).
Furthermore, In AzureFD I don't see 'X-Forwarded-Host' header begin populated, only 'X-Forwarded-For' (added some debug code to print all headers).
Is this right?
Comments are closed.
We're doing something a bit more complex in that each microservice we deploy could be at a random location in the API depending on if it's internal, external or separately hosted; plus we reserve the right to move things around in the API for testing, development or deployment purpsoes ;) So essentially - each service is being told where it is hosted, rather than be deployed at a static, fixed base path.
.NET Core has handled this complexity really, really well - most of the time (Apart from the noted bug), it 'just works™'.