Tag: SEO

Managing Redirects in Sitecore

One of the most important aspects if not the most important aspect to managing a website is SEO. It doesn’t matter how good your website is, it doesn’t really matter if nobody can find it. Creating good SEO is a lot about having pages match what users are searching for, which then results in a high ranking for those pages. However, a second part to this formula is what happens with a page when it no longer exists. For instance, one day this blog post may no longer be relevant to have on our site, but up until that point search engines will have crawled it, people will have posted links to it and all of this adds to its value which we would to lose should it be removed. An even simpler scenario could be that we rename the blog section to something else and the page still exists under a different URL, but links to it no longer work.

Fortunately, the internet has a way of dealing with this and it’s in the form of a 301 redirect. 301 redirect are an instruction to anyone visiting the URL that the page has permanently moved to a different URL. When you set up a 301 redirect it achieves two things. Firstly, users following a link to your site end up on a relevant page rather a generic page not found message. Secondly search engines recrawling your site update their index with the new page and transfer all the associated value. So, to avoid losing any value associated with a page, whenever you move, rename or remove a page you should also set up a redirect.

Sitecore

Given how important redirects are it’s then surprising that an enterprise CMS platform like Sitecore doesn’t come with a universal way to set redirects up out of the box.

Typically, when we’ve taken over a site one of the support requests we will eventually receive is a question surrounding setting up a redirect from a marketer who’s ended up on Sitecore’s documentation site and is confused why they can’t find any of the things in this article https://doc.sitecore.com/users/sxa/17/sitecore-experience-accelerator/en/map-a-url-redirect.html.

The article appears to suggest that creating redirects is a feature that is supported, however it’s only available to sites built using Sitecore Experience Accelerator rather than the traditional approach.

More often that not, the developers who created the site will have added 301 redirect functionalities but will have done it using the IIS Redirect module, which while serves the purpose of creating redirects does not provide a Sitecore interface for doing so.

301 Redirect Module

Fortunately, there are some open source modules available which add the missing feature to Sitecore. The one we typically work with is neatly named 301 Redirect Module.

Once installed the module can be found in the System > Modules folder. Clicking on the Redirects folder will give you the option to either create another Redirect Folder to help group your redirect into more manageable folders, Redirect URL, Redirect Pattern or Redirect Rule.

This is by far the most common redirect to be set up. You specify an exact URL that is to be redirected and set the destination as either another exact URL or a Sitecore item. There’s even an option to choose if it should be a permanent or temporary redirect.

Redirect patterns are slightly more complex and require some knowledge of being able to write regular expressions. However, these are most useful if you ever want to move an entire section of your site. Rather than creating rules for each of the pages under a particular page, one rule can redirect all of them. Not only does this save time when creating the redirects, but it also makes your list of redirects far more manageable.

Auto Generated Rules

One of my favourite features of the 301 Redirect Module is that it can auto generate rules for any pages you move. For instance, if you decide a sub-page should become a top-level page moving it will result in a rule automatically being generated without you having to do a thing.

Summary

A marketer’s ability to manage 301 Redirect is as important as them being able to edit the content on the site and while traditional Sitecore sites may be missing this functionality it can easily be added using 3rd party modules, best of all they’re free!

Removing port 443 from urls generated by Sitecore

For as long as I've been working on Sitecore there has been this really annoying issue where setting the link manager to include server url and running under https will cause urls to be generated with the port number included. e.g. https://www.himynameistim.com:443/ which naturally you don't actually want.

To overcome this there are a few methods you can take.

Method 1 - Set the Scheme and Port on you site defenition

This is possibly the smallest change you can make as it's just 2 settings in a config file.

Setting the external port on site node to 80 (yes 80) tricks the link manager code into not appending the port number as it does it for everything other than port 80.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<sitecore>
  <sites xdt:Transform="Insert">
    <site name="website">
      <patch:attribute name="hostName">www.MySite.com</patch:attribute>
      <patch:attribute name="rootPath">/sitecore/content/MySite</patch:attribute>
      <patch:attribute name="scheme">https</patch:attribute>
      <patch:attribute name="externalPort">80</patch:attribute>
    </site>     
  </sites>
</sitecore>
</configuration>

What I don't like about this method though, is your setting something to be wrong to get something else to come out right. It's all a bit wrong.

Method 2 - Write your own link provider

The second method which I have generally done is to write your own provider which strips the port number off the generated URL.

For this you will need:

1. A patch file to add the provider:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
  <linkManager defaultProvider="sitecore">
    <patch:attribute
        name="defaultProvider"
        value="CustomLinkProvider" />
    <providers>
      <add name="CustomLinkProvider"
           type="MySite.Services.CustomLinkProvider, 
                       MySite"
                     languageEmbedding="never"
                     lowercaseUrls="true"
                     useDisplayName="true"
                     alwaysIncludeServerUrl="true"
        />
    </providers>
  </linkManager>
  <mediaLibrary>
    <mediaProvider>
      <patch:attribute name="type">
        MySite.Services.NoSslPortMediaProvider, MySite
      </patch:attribute>
    </mediaProvider>
  </mediaLibrary>
</sitecore>
</configuration>

2. A helper method that removes the SSL port

namespace MySite
{
  /// <summary> 
  /// Link Helper is used to remove SSL Port 
  /// </summary> 
  public static class LinkHelper
  {
      /// <summary> 
      /// This method removes the 443 port number from url 
      /// </summary> 
      /// <param name="url">The url string being evaluated</param> 
      /// <returns>An updated URL minus 443 port number</returns> 
      public static string RemoveSslPort(string url)
      {
          if (string.IsNullOrWhiteSpace(url))
          {
              return url;
          }

          if (url.Contains(":443"))
          {
              url = url.Replace(":443", string.Empty);
          }

          return url;
      }
  }
}

3. The custom link provider which first gets the item URL the regular way and then strips the SSL port

using Sitecore.Data.Items;
using Sitecore.Links;

namespace MySite
{
  /// <summary>Provide links for resources.</summary> 
  public class CustomLinkProvider : LinkProvider
  {
      public override string GetItemUrl(Item item, UrlOptions options)
      {
          // Some code which manipulates and exams the item...

          return LinkHelper.RemoveSslPort(base.GetItemUrl(item, options));
      }
  }
}

4. The same provider for media

using Sitecore.Data.Items;
using Sitecore.Resources.Media;

namespace MySite
{
  /// <summary> 
  /// This method removes SSL port number from Media Item URLs 
  /// </summary> 
  public class NoSslPortMediaProvider : MediaProvider
  {
      /// <summary> 
      /// Overrides Url mechanism for Media Items 
      /// </summary> 
      /// <param name="item">Sitecore Media Item</param> 
      /// <param name="options">Sitecore Media Url Options object</param> 
      /// <returns>Updated Media Item URL minus 443 port</returns> 

      public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
      {
          var mediaUrl = base.GetMediaUrl(item, options);
          return LinkHelper.RemoveSslPort(mediaUrl);
      }
  }
}

What I don't like about this method is it's messy in the opposite way. The port number is still being added, and we're just adding code to try and fix it after.

Credit to Sabo413 for the code in this example

Method 3 - Official Sitecore Patch

Given that it's Sitecore's bug, it does actually make sense that they fix it. After all people are paying a license fee for support! This simplifies your solution down to 1 extra patch file and a dll. What's better is as it's Sitecores code they have the responsibility of fixing it, if it ever breaks something, and you have less custom code in your repo.

You can get the fix here for Sitecore version 8.1 - 9.0.

So this may leave you wondering how did Sitecore fix it? Well having a look inside the dll reveals they wen't for method 2.

Sitecore: Returning a 404 response on the page requested rather than redirecting to a 404 page

Previously I've blogged about:

but while looking through the posts today, I realised I had never written about how you stop Sitecore from issuing 302 redirects to your 404 page and instead return a 404 on the URL requested with the contents of the 404 page.

While search engines will recognise a 302 response to a 404 as a 404 (in fact they're intelligent enough to work out that a 404 page without a correct response status code is a 404) it's considered SEO best practice for the URL to stay the same and to issue the correct status code.

Creating a NotFoundResolver class

When Sitecore processes a request it will run the httpRequestBegin pipeline, and within that pipeline is a Item Resolver processor that will attempt to find the requested item. If after this the context item is still null then the logic to redirect to the ItemNotFoundUrl will kick in. To stop this happening we can simply add our own process to the pipeline after ItemResolver and set the item.

Our class looks like this:

using Sitecore;
using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Diagnostics;
using Sitecore.Pipelines.HttpRequest;

namespace Pipelines.HttpRequest
{
  public class NotFoundResolver : HttpRequestProcessor
  {
      private static readonly string PageNotFoundID = Settings.GetSetting("PageNotFound");

      public override void Process(HttpRequestArgs args)
      {
          Assert.ArgumentNotNull(args, nameof(args));

          if ((Context.Item != null) || (Context.Database == null))
              return;

          if (args.Url.FilePath.StartsWith("/~/"))
              return;

          var notFoundPage = Context.Database.GetItem(new ID(PageNotFoundID));
          if (notFoundPage == null)
              return;

          args.ProcessorItem = notFoundPage;
          Context.Item = notFoundPage;
      }
  }
}

To add our process to the pipeline we can use a patch file like this:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
  <pipelines>
    <httpRequestBegin>
      <processor
        patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"
        type="LabSitecore.Core.Pipelines.NotFoundResolver, LabSitecore.Core"  />
    </httpRequestBegin>
  </pipelines>
  <settings>
    <!-- Page Not Found Item Id -->
    <setting name="PageNotFound" value="ID OF YOUR 404 PAGE" />
  </settings>
</sitecore>
</configuration>

Notice the setting for the ID of your 404 page to be loaded as the content.

Remember if you do do this make sure you also follow one of the methods to return a 404 status code, otherwise you will have just made every URL a valid 200 response on your site.