Tag Archives: sitecore pipeline processor google search index trailing slash

To slash or not to slash (Website Urls)

I was updating a custom LinkProvider for Sitecore today and began wondering about whether or not to use a trailing slash on the generated Urls. Of course the first search result gave me the official recommendation from Google:


I hadn’t ever considered the implications that such a minor difference could have on indexing and SEO. If links with both formats get out there, you’re going to end up with two valid index entries which although may not have a great impact, if it could be avoided, then why not?

So, a lunchtime project! I’ll add a redirect at the start of each request that creates a 301 from one Url to the other. I decided to remove all trailing slashes, as that way I can test any Url for the existance of one and remove it (which is a lot easier than defining the logic about where to add one). It brought me straight to another fork in the road however. If I want to set up a redirect from one to the other, do I created a .NET HttpModule and add a BeginRequest handler, or do I create a Sitecore Pipeline Processor and wedge it into the PreprocessRequest pipeline?

Personally I like to go with whatever performs best and if I can find somewhere earlier in the request lifecycle to put this kind of thing I like to do so. I created one of each and let the debugger break at the first one it reached.

In my initial test, Sitecore won, but why? Well, I’d added my HttpModule at the end of the list in the web.config. Once I moved it above the Sitecore.Web.RewriteModule, it hit my module first. I decided to go with the .NET HttpModule, as I can now reuse it in non-Sitecore projects.

This is what it looks like:

    public class TrailingSlashModule : IHttpModule
        private HttpApplication _app;

        public void Init(HttpApplication app)
            _app = app;
            _app.BeginRequest += new EventHandler(OnBeginRequest);

        public void OnBeginRequest(object sender, EventArgs e)
            var pathAndQuery = _app.Context.Request.RawUrl.Split("?".ToCharArray());
            string absPath = pathAndQuery[0];
            string query = pathAndQuery.Length > 1 ? pathAndQuery[1] : string.Empty;
            if (absPath.EndsWith("/") || absPath.EndsWith("\\"))
                string url = absPath.TrimEnd('\\').TrimEnd('/') + (string.IsNullOrEmpty(query) ? "" : "?" + query);
                _app.Context.Response.Status = "301 Moved Permanently";
                _app.Context.Response.StatusCode = 301;
                _app.Context.Response.AddHeader("Location", url);

        public void Dispose()