In this post I describe how you can redirect requests to https and www or non-www with a rewrite rule in ASP.NET Core. The reasons to do a 301 redirect is improved SEO and higher website security.
It is better to have only one version of your website in search engines, if you have several versions of your website (http://, http://www., https://, https://www.) you might be penalized for duplicate content.
Hyper Text Transfer Protocol Secure (HTTPS) is a secure version of the Hyper Text Transfer Protocol (HTTP). Https means that all communication between a browser and your server is encrypted, this makes it very difficult for a man in the middle to get information from data that is sent between a browser and a server.
Why a 301 redirect? A 301 redirect is a permanent redirect from one url to another url, this means that search engines knows that the url that you redirect to is the correct url and that all search ranking score should apply to this url.
Redirect rule
I have created a rewrite rule that is responsible for redirects. I have used a Middleware for redirections before, but it stopped to work when I was upgrading from ASP.NET Core 2.2 to ASP.NET Core 3.1. This rule will only make one redirection, I think that it is better to only do one redirect compared to do multiple redirects if we use multiple rules.
using System;
using Microsoft.Net.Http.Headers;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite;
namespace Annytab.Rules
{
/// <summary>
/// This class handles redirects to https, www, non-www
/// </summary>
public class RedirectHttpsWwwNonWwwRule : IRule
{
#region Variables
public int status_code { get; set; }
public bool redirect_to_www { get; set; }
public bool redirect_to_non_www { get; set; }
public bool redirect_to_https { get; set; }
public string[] hosts_to_ignore { get; set; }
#endregion
#region Constructors
/// <summary>
/// Create a new rule
/// </summary>
public RedirectHttpsWwwNonWwwRule()
{
// Set values for instance variables
this.status_code = 301;
this.redirect_to_www = false;
this.redirect_to_non_www = false;
this.redirect_to_https = false;
this.hosts_to_ignore = new string[] { "localhost" };
} // End of the constructor
#endregion
#region methods
/// <summary>
/// Apply the rule
/// </summary>
public virtual void ApplyRule(RewriteContext context)
{
// Get the request
HttpRequest req = context.HttpContext.Request;
// Get the host
string host = req.Host.Host;
// Create an uri builder with the current request
UriBuilder uriBuilder = new UriBuilder(host + req.Path + req.QueryString);
uriBuilder.Scheme = req.Scheme;
uriBuilder.Port = req.IsHttps == true ? 443 : 80;
// Check hosts to ignore
for (int i = 0; i < hosts_to_ignore.Length; i++)
{
if (host.Equals(hosts_to_ignore[i], StringComparison.OrdinalIgnoreCase))
{
context.Result = RuleResult.ContinueRules;
return;
}
}
// Check if we should do a https redirect
if (this.redirect_to_https == true && req.IsHttps == false)
{
// Add https scheme and port
uriBuilder.Scheme = "https";
uriBuilder.Port = 443;
// Check if we should do a www redirect
if(this.redirect_to_www == true && this.redirect_to_non_www == false && host.StartsWith("www") == false)
{
uriBuilder.Host = "www." + uriBuilder.Host;
}
else if (this.redirect_to_non_www == true && this.redirect_to_www == false && host.StartsWith("www") == true)
{
uriBuilder.Host = uriBuilder.Host.Replace("www.", "");
}
// Do a redirect
HttpResponse response = context.HttpContext.Response;
response.StatusCode = this.status_code;
response.Headers[HeaderNames.Location] = uriBuilder.Uri.AbsoluteUri;
context.Result = RuleResult.EndResponse;
return;
}
else if (this.redirect_to_www == true && this.redirect_to_non_www == false && host.StartsWith("www.") == false)
{
// Modify the host
uriBuilder.Host = "www." + uriBuilder.Host;
// Do a redirect
HttpResponse response = context.HttpContext.Response;
response.StatusCode = this.status_code;
response.Headers[HeaderNames.Location] = uriBuilder.Uri.AbsoluteUri;
context.Result = RuleResult.EndResponse;
return;
}
else if (this.redirect_to_non_www == true && this.redirect_to_www == false && host.StartsWith("www.") == true)
{
// Modify the url
uriBuilder.Host = uriBuilder.Host.Replace("www.", "");
// Do a redirect
HttpResponse response = context.HttpContext.Response;
response.StatusCode = this.status_code;
response.Headers[HeaderNames.Location] = uriBuilder.Uri.AbsoluteUri;
context.Result = RuleResult.EndResponse;
return;
}
else
{
context.Result = RuleResult.ContinueRules;
return;
}
} // End of the ApplyRule method
#endregion
} // End of the class
} // End of the namespace
Use the redirect rule
Our rewrite rule is added to the Configure method in the StartUp class and this rule will be used to redirect to https and to www or non-www. I use IWebsiteSettingRepository
website_settings_repository to store information about if I should do a www redirect, a non-www redirect and/or a https redirect. KeyStringList is a wrapper for a dictionary with strings, this class makes it easier to get values from the dictionary.
// Add redirection and use a rewriter
RedirectHttpsWwwNonWwwRule rule = new RedirectHttpsWwwNonWwwRule
{
status_code = 301,
redirect_to_https = redirect_https,
redirect_to_www = redirect_www,
redirect_to_non_www = redirect_non_www,
hosts_to_ignore = new string[] {"localhost", "fotbollstabeller001.azurewebsites.net" }
};
RewriteOptions options = new RewriteOptions();
options.Rules.Add(rule);
app.UseRewriter(options);
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IWebsiteSettingRepository website_settings_repository)
{
// Use error handling
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseStatusCodePagesWithReExecute("/home/error/{0}");
}
// Get website settings
KeyStringList settings = website_settings_repository.GetAllFromCache();
bool redirect_https = settings.Get("REDIRECT-HTTPS") == "true" ? true : false;
bool redirect_www = settings.Get("REDIRECT-WWW") == "true" ? true : false;
bool redirect_non_www = settings.Get("REDIRECT-NON-WWW") == "true" ? true : false;
// Add redirection and use a rewriter
RedirectHttpsWwwNonWwwRule rule = new RedirectHttpsWwwNonWwwRule
{
status_code = 301,
redirect_to_https = redirect_https,
redirect_to_www = redirect_www,
redirect_to_non_www = redirect_non_www,
hosts_to_ignore = new string[] {"localhost", "fotbollstabeller001.azurewebsites.net" }
};
RewriteOptions options = new RewriteOptions();
options.Rules.Add(rule);
app.UseRewriter(options);
// Use static files
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
// Cache static files for 30 days
ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=25920000");
ctx.Context.Response.Headers.Append("Expires", DateTime.UtcNow.AddDays(300).ToString("R", CultureInfo.InvariantCulture));
}
});
// Use sessions
app.UseSession();
// For most apps, calls to UseAuthentication, UseAuthorization, and UseCors must
// appear between the calls to UseRouting and UseEndpoints to be effective.
app.UseRouting();
// Use authentication and authorization middlewares
app.UseAuthentication();
app.UseAuthorization();
// Routing endpoints
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
"default",
"{controller=home}/{action=index}/{id?}");
});
} // End of the Configure method
Do you have any video of that? I’d want to find
out more details.
thanks for your grate article
Great job, it worked like a charm. Thanks a lot!
how do i initialize IWebsiteSettingRepository
Hi,
you can remove it and implement your own handler for settings.
If you would like to get much from this piece of
writing then you have to apply these techniques to your won web site.