Enforcing a canonical host name on Azure

Maintaining a single host on azure so your visitors only ever experience your website through a single domain.

When we create an azure website, it is setup as yourname.azurewebsites.net. We can then add a custom domain name i.e. yourname.com. Your site now responds on both domains, this isn’t optimal, we want to consolidate this so visitors only ever experience our website through yourname.com.

As with all things .Net there are seemingly endless potential points at which we can hook into this process, however, we want to limit the amount of code that is executed before we potentially redirect our request.

I think the Application_BeginRequest event is best suited to handle this, and we have everything we need at this point. In our Global.asax.cs file we can add the following.

void Application_BeginRequest(object sender, EventArgs e)
{
  Context.RedirectToCanonical();
}

The RedirectToCanonical extension method is where we’ll add all our logic.

public static class CanonicalHostExtensions
{
    private static NameValueCollection Settings
    {
        get { return ConfigurationManager.AppSettings; }
    }

    private static bool RedirectEnabled
    {
        get
        {
            return Settings["CanonicalHost:Enabled"] == "true";
        }
    }

    private static string Host
    {
        get
        {
            return Settings["CanonicalHost:Hostname"];
        }
    }

    private static bool RequiresSecureConnection
    {
        get
        {
            return Settings["CanonicalHost:RequiresSecureConnection"] == "true";
        }
    }

    public static void RedirectToCanonical(this HttpContext context)
    {
        var request = context.Request;
        var response = context.Response;
        
        if (!RedirectEnabled)
            return;
            
        var host = Host;

        if (request.IsCanonical(host))
            return;

        response.RedirectPermanent(BuildCanonicalUri(request.Url, host, RequiresSecureConnection), true);
    }

    private static bool IsCanonical(this HttpRequest request, string expectedHost)
    {
        if (string.IsNullOrWhiteSpace(expectedHost))
            return true;

        var hostMatches = expectedHost == request.Url.Host;
        var protocolMatches = (RequiresSecureConnection && request.IsSecureConnection) || !RequiresSecureConnection;

        return hostMatches && protocolMatches;
    }

    private static string BuildCanonicalUri(Uri url, string host, bool requiresSecureConnection)
    {
        var uri = new UriBuilder(url)
        {
            Host = host
        };

        if (uri.Uri.IsDefaultPort)
            uri.Port = -1;

        if (requiresSecureConnection)
            uri.Scheme = "https";

        return uri.ToString();
    }
}

Here we check if we have enabled redirection, whether the request URL is already canonical, and finally if it’s not we’ll create a canonical version of the URL. It’s worth noting that in this code I’m handling the upgrade to https, as I don’t want to issue unnecessary redirects as that kills our page speed.

Finally, we need to add the following appSettings to our web.config, to allow us to configure whether the redirect is enabled, our canonical host name and whether or not we should upgrade requests to a secure connection.

<configuration>
  <appSettings> 
    <add key="CanonicalHost:Enabled" value="false" />
    <add key="CanonicalHost:HostName" value="yourhostname.com"/>
    <add key="CanonicalHost:RequiresSecureConnection" value="true" />
  </appSettings>
</configuration>

As I mentioned at the start of this post there are many ways to this problem, this solution happened to be the best fit for my requirements. If you’ve got any questions or suggestions for improvements, why not tweet me @benembery.