A A A Font Size

Pearls of MOSSy Wisdom

Fixing the 302 redirect problem in SharePoint

Updated 7-1-8.
Updated 9-3-8
 
I have been trying to figure out how to change SharePoint's default 302 redirect to a 301 redirect for a coupld of weeks now in order to help my site's search-friendliness.  For those of you that don't know, SharePoint handles all it's redirects with 302 redirects.  These are not liked by search engines.  So while SharePoint's search doesn't have a problem with this, Google's does, so this is a problem that I need to address. 
 
There is help online, but I have to admit that I had some trouble with the code provided.  Thankfully, I've been able to modify the code online in order to get it working for a more general use. 
 
First off, let me say that the basis for this code is not mine.  I found it at:
Here Mr. Mastykarz does a good job solving this problem for his environment.  What I am presenting here is a modified version of his code that doesn't have all of the items that are either specific to his environment or hardcoded into the code.  That being said, I am not going to explain the code as Waldek Mastykarz did a very good job explaining what the code does in his blog post and I don't want to steal all the credit from Mr. Mastykarz. 
 
The code presented is a C# Class file that you'll need to create/compile just like the original.  What I will do is present my changes and final code listing for what I believe make this solution more suable for a generic environment. 
 
Now, since I am presenting a new version of someone else's code, what I will do is explain my changes. 
 
First and foremost, I removed his handling for Variations as I feel his method is not a good one for general use since he used a (hard coded) non-standard language label ("nl") and hardcoded a couple other items into the redirect paths that do not apply to every SharePoint installation.  That being said, the entirety of his Variation-handling code has been removed.  This is instead handled in by modifying the VariationRootLanding.ascx file.  This will be expanded upon below. 
 
Second, I took out an amendment he added to his code to handle Infopath and Sharepoint Designer (which registers as Frontpage, oddly).  I could not replicate the problem this was added to solve and, in fact, it being there caused an error when I tried to use the code, so it was removed. 
 
After making these changes I compliled the class and placed it in the BIN directory of site site.  Note, this did not work when I placed the control in the GAC.  So if you have multiple sites in your farm, you're going to have multiple copies of this control. 
 
After the control was installed, one other change was made that involved the use of Elevated Privlidges in the code.  This is a problem that needed to be addressed as the original code will not run correctly otherwise. 
 
For my situation, and most people's situation I would think, the use of Elevated Permissions is not allowed in Medium (or lower) trust levels.  Since I am placing this on an internet-facing, anonymous site, it definately didn't work for me.  That being said, I had to create a Custom CAS entry to allow this to run in my site's trust level.  For how to do this, see my previous blog post.
 
In addition to creating a custom CAS entry, you also have to edit your web.config file to tell your site to use this method for redirects.  To do this, edit your site's web.config and scroll down until you find the httpModules section.  Just after the <clear /> tag, add in the following:
<add name="CustomRedirectModule" type="Custom.Sharepoint.RedirectFix.RedirectModule" />
 
Note, this has to be the first on after the clear to make sure it overrides any other redirct methods in other httpModules. 
 
Save the web.config and you're now using this module to handle your redirects.  I had to write a quick application to test the redirect hops to insure the 301's were being used and not the 302's.  If you want this, let me know and I'll post it in my downloads section.  
 
UPDATE: 9-3-8.  I posted the Hop Tester.
 
Now the control's code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Text.RegularExpressions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;

namespace Custom.Sharepoint.RedirectFix
{
    public class RedirectModule : IHttpModule 
    {
        #region IHttpModule Members   
        public void Dispose() { }   
       
        public void Init(HttpApplication context)   
        {     
            context.BeginRequest += new EventHandler(context_BeginRequest);   
        }
        void context_BeginRequest(object sender, EventArgs e)   
        {     
            HttpApplication app = (HttpApplication)sender;
            string userAgent = app.Request.UserAgent.ToLowerInvariant();
            if (userAgent.Contains("infopath") || userAgent.Contains("frontpage"))
                return;
            string requestUrl = app.Request.Url.ToString();
            Regex regEx = new Regex(@"^https?://.*(?<itemUrl>/[^/]+\.[^/\.]+)$");
            if (regEx.IsMatch(requestUrl))
                return;     
            if (!requestUrl.EndsWith("/", StringComparison.CurrentCulture))
                requestUrl += "/";     
            string destinationUrl = String.Empty;
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                try
                {
                    using (SPSite site = new SPSite(requestUrl))
                    {
                        using (SPWeb web = site.OpenWeb())
                        {
                            if (PublishingWeb.IsPublishingWeb(web))
                            {
                                PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);
                                destinationUrl = String.Concat(requestUrl, publishingWeb.DefaultPage.Url);
                            }
                            else
                            {
                                destinationUrl = String.Concat(requestUrl, "index.aspx");
                                destinationUrl = String.Concat(requestUrl, web.RootFolder.WelcomePage.ToString().Substring(web.RootFolder.WelcomePage.ToString().LastIndexOf('.') + 1));
                            }
                        }
                    }
                }
                catch { }
            });
            if (!String.IsNullOrEmpty(destinationUrl))
            {
                app.Response.AddHeader("Location", destinationUrl);
                app.Response.StatusCode = 301;
            }   
        }   
        #endregion 
    }//class
}//namespace
Now, if you don't use variations, you're done.  However, if you do have a site that is variation enabled, to handle the variations aspect, you will need to modify the VariationRootLogic.ascx file. 
 
This file is found in the 12 Hive's TEMPLATE\CONTROLTEMPLATES folder.  This file is easily editable in Visual Studio.  To edit it, open the ASCX file and find the OnLoad function.  It will look like this: 

protected override void OnLoad(EventArgs e)
{
  base.OnLoad(e);
  string targetUrl = this.GetRedirectTargetUrl();
  if (!string.IsNullOrEmpty(targetUrl))
  {
     SPUtility.Redirect(targetUrl, SPRedirectFlags.Default, Context);
 }
}
Comment out (or just remove) the SPUtility line and replace it with the following:
Context.Response.Status = "301 Moved Permanently";
Context.Response.AddHeader("Location",targetUrl);
making the final funtion look like:
protected override void OnLoad(EventArgs e)
{
 base.OnLoad(e);
 string targetUrl = this.GetRedirectTargetUrl();
 if (!string.IsNullOrEmpty(targetUrl))
 {
//SPUtility.Redirect(targetUrl, SPRedirectFlags.Default, Context);
Context.Response.Status = "301 Moved Permanently";
                Context.Response.AddHeader("Location",targetUrl);
 }
}
That's it.  Save this file and you're good to go.  I hope this helps and thank you to Waldek Mastykarz for getting me started on this.
 
UPDATE (7-1-8):
Apparently it took a while to notice this, but I needed to put back some code that I took out of the original code.  In the code above, there is now a highlighted (purple and italics) section that allows for InfoPath and SharePoint Designer to connect properly.  Apaprently, the VM Sandbox didn't un-cache itself until last night and this piece of code that caused me trouble turned out to be needed.  Apaprently, if SPD finds that you've 301'd your site, it think it has been moved permantly and won't let you connect.  So this is needed. 
 
For those of you writing your own redirect hop tester, make sure you set your HttpWebRequest.UserAgent to something as a NULL (default) value will cause an error.
You must sign in to rate content.
(Unrated)

Comments

Thank You!

Fantastic post Tim! Thank you for this great solution to a very common (and very real) SEO related problem.
Sean Bordner at 6/27/2008 12:44 PM
You must sign in to rate content.
(Unrated)

Thank You!

Fantastic post Tim! Thank you for this great solution to a very common (and very real) SEO related problem.
Sean Bordner at 6/27/2008 12:44 PM
You must sign in to rate content.
(Unrated)

Thank You!

Fantastic post Tim! Thank you for this great solution to a very common (and very real) SEO related problem.
Sean Bordner at 6/27/2008 12:44 PM
You must sign in to rate content.
(Unrated)

Leave a Comment

You must be logged in to post a comment.
Superstar
Tim Dobrinski
Systems Analyst II
Birmingham, AL
Tim is currently not Twittering about updating his LinkedIn profile concerning FaceBooking his MySpace Page's Status.

Search This Blog

 

© 2010 SusQtech. All rights reserved.
Powered by SharePoint Server 2007 and using the MemberToMember SharePoint Add-On for social media capabilities.