Handling 404 in a Sitecore multisite solution and avoid 302 redirects

This post shows how to handle a request for a page which is not found, how to avoid having the request being redirected with a 302 status code before a 404 and finally how to use different pages within Sitecore for not found pages in a multisite solution.

The post is based on some code I wrote back in June 2012 and I have seen the concept used several places since then so a lot of you will know it already and might already have it implemented yourselves in some form.

My colleague Brian already wrote a post about the basic concept here in his post Sitecore 404 without 302 but I will go a bit further into details and extend the code which he is showing.

The code presented in this post can be downloaded as a module on Sitecore marketplace. Note that this code is well tested and have been running on several production sites for the last couple of years.

What is wrong with a 302 status code?

Basically it all comes down to SEO. If a search bot follows a link and receive a 302 then all is good on that url since it has just been temporarily moved to another address.

This means that the requested url’s which responds with a 302 is kept in the search indexes which is all expected behaviour even though the real intent was to get the url removed from the index since it no longer exists.

Very bad 404 implementation

To get the url removed from the search indexes then it has to respond right away with a 404 when requested and not with a 302 followed by a 404.

Unfortunately this 302 status code followed by a 404 is seen very often on quite a lot of Sitecore installations due to improper configuration or bad coding. It is actually the out-of-the-box behaviour in Sitecore if you do not change it to use server transfers instead of redirects.

404 not found page settings in Sitecore

Sitecore have not just one but two settings in which you can point to an URL on the website when a 404 status code is needed.

Both of these settings are global for the entire solution and cannot be set for individual sites in a multisite solution.

You can furthermore configure Sitecore to use Server.Transfer instead of Response.Redirect which will avoid the 302 status code.

This should always be set to true if you do not use this module or something similar.

It still does not solve that sites in a multisite solution share settings thus share the relative url for when a page is not found.

Coding a multisite friendly 404 handling

What is it we want to acheive exactly?

When an item is not found by the ItemResolver or if the current item does not have a layout then we would like the response to have status code 404 and we want to show a nice looking page which the editors can control for each site in a solution.

First we have to add a notFoundItem property for site definitions in Sitecore.

We add the key for this property in a constants file and then we write a small service class which can read the property and load it as an item either from an ID, a local or a full path.

Now when this is ready we implement a HttpRequestProcessor and place in the HttpRequestBegin pipeline after the ItemResolver.

Note that we first check if a valid context item has been found and exit the processor quickly if one has been found. An item is not valid if it does not have a version in the context language or if it does not have a layout set. Checking for context language version uses the item extension method from this post. 

If the request is not for a valid item, or for something residing beneath /Sitecore or for a file then we set the context item to a site specific NotFoundItem.

Now if we would set the response status code to 404 at this early stage then Sitecore will take over control of the request so to avoid this we do not set the status code just yet.

Instead we write a small repository which can get and set a boolean value from the current request items.

Then we implement yet another HttpRequestProcessor which we place in the end httpRequestProcessed pipeline.

Finally we add an include config file for patching in the processors in the pipelines.

To get it working we of course need to fill in the notFoundItem Property on the sites on which the module should be used.

Example

Sum up

A nice feature of this module is that you will get a 404 and a nice looking page whatever nonsense you type into the address bar if it does not resolve to a valid item or file. Just as when you use the server side error redirects.

Good 404 on a nice looking item

As mentioned in the beginning this code is somewhat old but I think it is extremely useful and it solves the somewhat lacking normal 404 error handling in an elegant manner.

The module can be downloaded from Sitecore marketplace here NotFoundItem 

Returning a 404 when no layout exists on the context item like I showed here breaks an out-of-the-box feature in Sitecore which I never have seen anyone use. That is the DefaultLayoutFile setting which can point to a fallback file to use as layout if no presentation details has been set on an item. Has anyone ever used this setting?

Anders Laub

Anders Laub Christoffersen

Anders has been working with Sitecore for over a decade and has in this time been the lead developer and architect on several large scale enterprise solutions all around the world. Anders has been nominated a Sitecore Technical MVP three years in a row for 2014, 2015 and 2016. Anders is now working as a Sr. Solutions Architect at Sitecore in Copenhagen.

16 thoughts on “Handling 404 in a Sitecore multisite solution and avoid 302 redirects

  1. Very nice post Anders.

    However, there is one thing missing. Handling a multi language site.

    Adding a simple check in the “IsValidContextItemResolved” fixes this:

    if (Sitecore.Context.Item.Versions.GetVersions().Length == 0)
    return false;

    Keep up the good blogging!

  2. Hi Anders,

    This is a great contribution, though my company was wondering if there is a way to extend the functionality to support missing items from the media library?

    Thanks

    • Hi Mike,

      This approach does work for media items just fine, if the item does not exist it will set the notfounditem as the context item and send a 404 status code.

      If you are using the code and it does not work for media items in your solution please write to me either on twitter @AndersLaub or anders.laub@gmail.com and I’ll can help you out.

      –Anders

  3. Pretty slick. Thanks for sharing this code.

    FYI, we’ve got this running on Sitecore 7.5.current, and thus far there have been no issues getting it working.

  4. Hi,

    We are using this module on Sitecore 8. For items that do not exist It works just fine. However for Items without a layout. I get an ‘object moved to here’.
    I am investigating what is going on but I am curious if anyone else encountered this issue.

    Joost

      • Sorry for the long reply.

        I have not experienced this myself and we are using this module on a lot of Sitecore 8.x solutions.

        Did you change the LayoutNotFoundUrl in settings? It might be this which interferes with the module somehow.

        Also in production we tend to modify the customErrors config section as follows:

        <customErrors xdt:Transform="Replace" mode="On" defaultRedirect="/500.html">
        <error statusCode="404" redirect="/404" />
        </customErrors>

        By convention we always call the NotFoundItem 404 and then we create a static 500.html page and place in the webroot.

        • I was getting this problem too (object moved for an item with no layout) in 8.1.
          I’m finding that having set the Context.Item as above, during the MVC pipeline it gets switched back to the original item, and tries to do a redirect. And then at the end when trying to set the status to a 404, already has a redirect on the response.

          Adding in a custom pipeline to mvc.getPageItem, and return the page item as the 404 item, where have already identified the page to be a 404, seems to have done the trick, and now I get the 404 response for items with no layouts.
          Only Just got this working, and testing this out.
          Thought I would share.
          Thoughts?

          • Hi Ian, we have it working just fine without any modifications on 8.1 MVC solutions so it must be something specific in your setup that causes this behaviour that you are describing.

        • In this case, what is /404? Since the not found can be different per site, are we just sending it to a non existing path?

          • Hi Ernesto, /404 is an example. The issue is that Sitecores standard item/layout not found settings does not support different paths on different sites. So to make it work the not found item has to be located on the same relative path on all sites. In this example, an item in the root of the site called 404.

  5. But if it the path is always the same relative path, you could use the same path in the sitecore settings for not found items and the item would be different per site without using the custom processor. Add the server.transfer setting and you could have the same solution without messing with the pipelines. Am I missing something?

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*
Website