Insights

Enable Smart And Safe Error Pages In Sitecore

Setting Up Your Error Pages

We've all seen them. Those amazingly cool 404 pages. Gorgeous to look at even though you hope no one ever sees them.

Then there are the functional ones, the ones that even add some smarts to the page, recognizing where the user was trying to go and giving them suggestions as to where they should look instead.

We've even created some ourselves. But written incorrectly, without awareness of the limitations of your own environment, and they can actually do more harm than good if you're not careful.

Simple Sitecore Steps

When it comes to Sitecore, it's not as simple as just updating a web.config. You actually have to update Sitecore configuration so it knows how to deal with things like 404 errors.

The Configuration

First, create a patch file such as the following where you tell Sitecore where to send users when they stumble upon a missing page or broken link or a page that doesn't have a layout assigned (the last one is almost always forgotten about).


    <?xml version="1.0"?>
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
      <sitecore>
        <settings>
          <setting name="LayoutNotFoundUrl">
            <patch:attribute name="value">/404</patch:attribute>
          </setting>
          <setting name="ItemNotFoundUrl">
            <patch:attribute name="value">/404</patch:attribute>
          </setting>
          <setting name="RequestErrors.UseServerSideRedirect">
            <patch:attribute name="value">true</patch:attribute>
          </setting>
        </settings>
      </sitecore>
    </configuration>
    

The Code

Now on that 404 item you will likely want to create a custom controller action and rendering so it displays a proper page that shares specific details, offers options, etc.

Keep in mind, you'll want to ensure it's safe enough to not cause a 500 error in the process of displaying it.

Status Codes

While combinging the above two steps is enough to give you a 404 page, it won't address the issue of status code.

The way it sits right now, if you entered a page that doesn't exist, the status code being returned will be a 200 error, NOT a 404.

People have solved this a number of ways:

Using A Pipeline Processor

Creating a pipeline processor patched after Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel that checks if the Sitecore.Context.Item returned is null, updates HttpContext.Current.Response.StatusCode = 404.

The pros of creating a pipeline processor are that you can package this up as a separate project and re-use it for other clients. No need to re-write it again and again.

The cons are that it's a tad bit more complex than other options. But is it really?

The patch file might look a bit like the following:


    <?xml version="1.0"?>
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
      <sitecore>
        <pipelines>
          <httpRequestBegin>
            <processor type="Fishtank.Pipelines.HttpRequest.ErrorNotFoundProcessor, Fishtank.Pipelines"
                patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']" />
          </httpRequestBegin>
        </pipelines>
      </sitecore>
    </configuration>
    

And code as simple or as complex as you want:


    public class ErrorNotFoundProcessor : Sitecore.Pipelines.HttpRequest.HttpRequestProcessor
    {
        public override void Process(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args)
        {
            if (Sitecore.Context.Item == null && Sitecore.Context.Database != null)
            {
                var errorNotFoundItem = Sitecore.Context.Database.GetItem(string.Concat(Sitecore.Context.Site.StartPath, Settings.ItemNotFoundUrl));
                if (errorNotFoundItem != null)
                {
                    HttpContext.Current.Response.StatusCode = (int) HttpStatusCode.NotFound;
                    Sitecore.Context.Item = errorNotFoundItem;
                }
            }
            return;
        }
    }
    

Just Updating The Controller Action

Updating the Controller Action that you setup that displays the 404 page to update HttpContext.Current.Response.StatusCode = 404 but understand that this can only be done if you set it prior to updating the body of the response otherwise you'll get a warning that you're trying to update Headers after they're being sent.

Its pro is that it's simple, the con is it's not very reusable and you really have to do your fair share of exception checking to ensure your 404 page doesn't result in a 500 exception.


        HttpContext.Response.Clear();
        HttpContext.Response.StatusCode = 400;
    

Which way you choose will be a preference of your development practices but obviously we'd lean towards a pipeline processor.

Somethings To Consider

If you're working in the .NET world, knowing what version of .NET you're running is extremely important - likely 4.62 or 4.71 depending on which version of Sitecore you're running.

Reason is if an error page itself is generating an error message while trying to display itself, it can lead your site down a spiral.

This can end in horrible performance, slow response times, and the piling up of error upon error such as the following indicating that the request queue is basically full.


    System.Web.HttpException (0x80004005): The request queue limit of the session is exceeded.
    at System.Web.SessionState.SessionStateModule.QueueRef()
    at System.Web.SessionState.SessionStateModule.PollLockedSession()
    at System.Web.SessionState.SessionStateModule.GetSessionStateItem()
    at System.Web.SessionState.SessionStateModule.BeginAcquireState(Object source, EventArgs e, AsyncCallback cb, Object extraData)
    

Microsoft talks briefly about this in a knowledge article titled: Retargeting Changes for Migration from .NET Framework 4.6.2 to 4.7 and while I encourage you to have a read, it's equally important to understand how your code may result in this happening.

Regardless of whether you're on 4.6.2 or 4.7.1 or any other version, having any kind of detailed error message like the above show on your site shows just a lack of awareness of what's happening in your code, testing of said code, and quality of work in general.

It looks bad on your client and looks worse upon you. And while errors do happen, no website is perfect, it's best to avoid where possible - be it improved null checking, utilizing try/catch methods to avoid, or displaying a safe error page.

And by safe, we mean an error page that doesn't have a chance to result in another error. When a 500 error has resulted it should show a 500 error, not result in another 500 error.

How to know if it is? Well, one way to know is you would see an error like the following.

Double Error Screenshot

Error Page Setup

So, firstly, how to does one setup an error page. Out of the box whether you're using IIS locally or Azure Web Services in the cloud, your Web.config file likely contains a line shown below:


    <customErrors mode="Off" />
    

With this as is, whenever you get an error on your site, you're going to going to see that gorgeous yellow error page detailing roughly what went wrong and where. Handy for development. Painful for production.

Updating it a bit more with some smarts such as the following will give your visitors some repreive from the yellow menace.

It's also wise to include not just 500 and 400 but 404 as well as not every file type will be interpretted by Sitecore and thus may leak through to the native IIS. As such, using this approach covers your bases to ensure a nice error is shown.


<customErrors mode="On" redirectMode="ResponseRewrite">
<error statusCode="500" redirect="/500.aspx"/>
<error statusCode="400" redirect="/400.aspx"/>
<error statusCode="404" redirect="/404.aspx"/>
</customErrors>

Before we move on, I want to point a few things out however:

  1. The use of redirectMode="ResponseRewrite" will ensure the url of the page your user was hitting doesn't get re-written to be the actual error page itself. No one likes that. Certainly not crawlers.
  2. Note how we're using .aspx here. You're probably saying, I thought you said a simple safe, error page. Why not use .html? Well, that's exaclty what these files are. They're plain html files with the extension renamed to .aspx.

When it comes to the html vs aspx ruling, a lot of it depends on the platform you're running. In some cases, .html is perfectly fine. But if you're seeing something resembling the following, where your error page is displaying raw html, don't fret.

500 Error

So how do you solve it? The trick is simple. Rename the .html file to .aspx and update the error redirect value accordingly.

What's Causing This?

The issue is due to whether or not you have allowed the .html file extension from being interpretted. Sitecore utilizes .aspx and not .html. You actually have to force it to allow for .html to be shown as intended.

How To Test Your 500 Error Page

When it comes to testing a 404 error page, it's simple. Just enter in a URL you know doesn't exist. But testing a 500 error can be a bit more challenging.

The easiest solution to test whether a 500 error results in the 500 error page you've designed is to utilize unsafe url characters.

This is by no means the best solution, but it's the fastest. You could also throw exceptions in your code to validate the error page works as well and in some cases, you will want to do that, over this solution.

Now, a couple of warnings:

  • They're unsafe characters for a reason as they can be quite harmful to your site causing error messages as well if abused, server degradation, and potentially uncaught exploits.
  • Depending on the platform, like Sitecore, you can actually use some of these in the URL as long as they're encoded. So knowing which ones are ok to use as part of the test is something you will want to check first.
Type Characters Needs Encoding?
Safe characters Alphanumerics [0-9a-zA-Z] and unreserved characters. You can also include only when they're being used in their intended purpose. NO
Unreserved characters _ ~ - . NO
Reserved characters : ! $ & ' ( ) / ? # ; = [ ] @ * + , YES - if not used for intended purposes
Unsafe characters Includes the blank/empty space and " < > | \ ^ ` % { } YES
ASCII Control characters This includes the 7F (127 decimal) and the ISO-8859-1 (ISO-Latin) character ranges 00-1F hex (0-31 decimal) YES
Non-ASCII characters Includes ISO-Latin set 80-FF hex (128-255 decimal) YES
Everything else Any character(s) that are not listed above should be perect "%" encoded. YES

Be Safe Out There!

So in general. Just be safe. While it can be really helpful and amazing to have a page that is both gorgeous and functional, it's just as important, if not more so, to ensure it's safe for your infrastructure as well.

Hey, Developers!

We're on the look out for talented developers to join our team.

Think you have what it takes?

Meet David Austin

Development Team Lead

📷🕹️👪

David is a decorated Development Team Lead with Sitecore Technology MVP and Coveo MVP awards, as well as Sitecore CDP & Personalize Certified. He's worked in IT for 25 years; everything ranging from Developer to Business Analyst to Group Lead helping manage everything from Intranet and Internet sites to facility management and application support. David is a dedicated family man who loves to spend time with his girls. He's also an avid photographer and loves to explore new places.

Connect with David