How to get base URL in ASP.NET Core

You typically don't and shouldn't need to know where a web app is deployed. At least not from within the code of the web app itself. I keep seeing questions related to this, though. There are a range of reasons why this can still be relevant like if you want to generate and output an absolute URL in an MVC controller or Razor page. Here's a blog post about how to get the base URL in ASP.NET Core.

How to get base URL in ASP.NET Core

Let's rewind a bit before we start looking into the code. All websites are deployed somewhere. This can be locally on your developer machine, test environment, production environment running on Azure, or something else. Common for all websites is that you will need a URL to access different pages on the website. Example:

https://example.com/path/document.js?id=42

The URL consists of a range of parts. https is the schema, example.com is the hostname, path/document.js is the path, and id=42 is the query string. When talking about the base URL that is the theme of discussion here, that part is a combination of the scheme, port, and hostname:

https://example.com

As you probably already know, the port number can be omitted as long as the web server listens on port 443 for https or port 80 for http requests.

With that out of the way, let's get to the point. So, how do we get access to this information in ASP.NET Core? To my knowledge, there are no built-in ways to get the base URL, but the request provides enough information to put it together manually. In MVC it's easy:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        var baseUri = $"{Request.Scheme}://{Request.Host}:{Request.Host.Port ?? 80}";
        return View();
    }
}

This example is what typically is proposed when people ask on Stack Overflow or similar. There are some downsides to using string interpolation like this. The obvious one is the port part which we in most cases don't want to be included. Luckily, .NET comes with a built-in class for creating URLs named UriBuilder. Let's refactor the code a bit:

var uriBuilder = new UriBuilder(Request.Scheme, Request.Host.Host, Request.Host.Port ?? -1);
if (uriBuilder.Uri.IsDefaultPort)
{
    uriBuilder.Port = -1;
}

var baseUri = uriBuilder.Uri.AbsoluteUri;

In this example, we use the UriBuilder class to add the scheme and hostname and only add the port if it's in the request. The value of -1 (strangely) represents no port in UriBuilder. The if-statement included is to avoid showing the port number in the base URL if it's the default for the scheme. So, in case scheme is https and port is 443 we just want https://example.com and not https://example.com:443.

Would your users appreciate fewer errors?

➡️ Reduce errors by 90% with elmah.io error logging and uptime monitoring ⬅️

To make this even simpler to use, we can include this code as an extension method on HttpRequest by creating a new class named HttpRequestExtensions:

public static class HttpRequestExtensions
{
    public static string? BaseUrl(this HttpRequest req)
    {
        if (req == null) return null;
        var uriBuilder = new UriBuilder(req.Scheme, req.Host.Host, req.Host.Port ?? -1);
        if (uriBuilder.Uri.IsDefaultPort)
        {
            uriBuilder.Port = -1;
        }

        return uriBuilder.Uri.AbsoluteUri;
    }
}

Getting hold of the base URL only requires a single line now.

In Razor Pages, you won't have access to the HTTP request like in MVC. To make this work, start by including the HttpContextAccessor in the Program.cs file (Startup.cs if you are on earlier versions of ASP.NET Core):

public static void Main(string[] args)
{
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddHttpContextAccessor();
    // ...
    var app = builder.Build();
    // ...
}

Once that is included you can inject the IHttpContextAccessor in the Razor page:

{
    private readonly IHttpContextAccessor httpContextAccessor;

    public IndexModel(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    public void OnGet()
    {
        var baseUrl = httpContextAccessor.HttpContext?.Request.BaseUrl();
        // ...
    }
}

elmah.io: Error logging and Uptime Monitoring for your web apps

This blog post is brought to you by elmah.io. elmah.io is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With elmah.io, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.

See how we can help you monitor your website for crashes Monitor your website