Most websites needs a policy for handling the www sub domain because of SEO. It doesn’t matter if you redirect all requests coming from http://www.example.com to http://example.com or the other way around. The important thing is that you do one of them. Otherwise search engines will punish you for duplicate content.

This feature is built in to BlogEngine.NET and it allows you to either enforce the use of www or to remove it. It’s an HttpModule that handles these two scenarios and I’ve pulled it out of BlogEngine.NET and polished it so it can be used in any ASP.NET web application.

The code

This is the bit that handles the incoming request and either removes or enforces the www sub domain based on a web.config appSetting.

/// <summary>

/// Handles the BeginRequest event of the context control.

/// </summary>

/// <param name="sender">The source of the event.</param>

/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>

private void context_BeginRequest(object sender, EventArgs e)

{

  string rule = ConfigurationManager.AppSettings.Get("WwwRule");

 

  HttpContext context = (sender as HttpApplication).Context;

  if (context.Request.HttpMethod != "GET" || context.Request.IsLocal)

    return;

 

  if (context.Request.PhysicalPath.EndsWith(".aspx", StringComparison.OrdinalIgnoreCase))

  {

    string url = context.Request.Url.ToString();

 

    if (url.Contains("://www.") && rule == "remove")

      RemoveWww(context);

 

    if (!url.Contains("://www.") && rule == "add")

      AddWww(context);

  }

}

 

/// <summary>

/// Adds the www subdomain to the request and redirects.

/// </summary>

private static void AddWww(HttpContext context)

{

  string url = context.Request.Url.ToString().Replace("://", "://www.");

  PermanentRedirect(url, context);

}

 

private static readonly Regex _Regex = new Regex("(http|https)://www\\.", RegexOptions.IgnoreCase | RegexOptions.Compiled);

 

/// <summary>

/// Removes the www subdomain from the request and redirects.

/// </summary>

private static void RemoveWww(HttpContext context)

{

  string url = context.Request.Url.ToString();

  if (_Regex.IsMatch(url))

  {

    url = _Regex.Replace(url, "$1://");

    PermanentRedirect(url, context);

  }

}

 

/// <summary>

/// Sends permanent redirection headers (301)

/// </summary>

private static void PermanentRedirect(string url, HttpContext context)

{

  context.Response.Clear();

  context.Response.StatusCode = 301;

  context.Response.AppendHeader("location", url);

  context.Response.End();

}

 

Implementation

Download the HttpModule below and drop it in your App_Code folder. Then register the module in the web.config’s system.web section like so:

<httpModules>
  <add type="WwwSubDomainModule" name="WwwSubDomainModule" />
</httpModules>

You also need to add an appSetting like so:

<appSettings>
  <!-- Values can be 'add' or 'remove' -->
  <add key="WwwRule" value="add"/>
</appSettings>

WwwSubDomainModule.zip (1,23 kb)

I’ve been working on the URL structure on the upcoming version of BlogEngine.NET. In the current version, the URL wasn’t very pretty when viewing posts on certain dates or all posts in a month. It looked like this for a specific date:

example.com/blog/?date=2007-12-19

and like this for a specific month in a year:

example.com/blog/?year=2007&month=12

That’s the way they have looked since BlogEngine.NET 1.0 and I must admit they look awful. I just never gave it much thought to improve on them.

Then the other day I gave it some thought and for some stupid reason, I thought it would be cool to use the PathInfo part of the URL instead of the query string. So now the URLs looked like this:

example.com/blog/default.aspx/2007/12/19/

and

example.com/blog/default.aspx/2007/12/

This was of course an improvement since they became much more similar and used the same logical structure. The problem is that by using the PathInfo without some kind of URL rewriting at the same time, the relative root URL changes from /blog/ into /blog/default.aspx/2007/12/19/ which of course ruins a lot of things.

Today I finally saw the light. I have no idea why it took me so long, but I guess that late is better than never. The URLs now look like this:

example.com/blog/2007/12/19/default.aspx

and

example.com/blog/2007/12/default.aspx

and because I saw the light I’ve also added the next logical step, which is to display all posts in a given year like so:

example.com/blog/2007/default.aspx

What’s really stupid is that every other blog platform uses this URL structure. I must have had my head up my butt. When I was at it I also changed the calendar URL from

example.com/blog/default.aspx?calendar=show

to

example.com/blog/calendar/default.aspx

All the old URLs still work, so no links will be broken.

The .aspx extension

Now you might wonder why the default.aspx is needed at the end of the URLs. If I had a choice about it, trust me, it would be history. The thing is that on hosted servers where you cannot touch the IIS you will not be able to pass all incoming requests to the ASP.NET ISAPI filter. Only a few extensions including .aspx do. So in order to make sure the URL will work on those servers, we need the .aspx extension. It didn’t have to be default.aspx it could be anything ending with .aspx, but I thought default.aspx was one people had seen before and wasn’t confused about.

We’ll have to wait for IIS 7 to be released before we can construct URLs without the .aspx extension on hosted servers.

You can test the new URL on this blog.