This is a task that virtually every site or application should account for but is almost always overlooked. For that reason, addressing it from the outset and having a standard and straight-forward approach makes it automatic. It should also, hopefully, be a solution that can work in a single site or multiple site so it needs to be both flexible and unintrusive.
First Things First
There are several built-in features from default Sitecore that should be addressed or possibly ignored because they don’t really add to the solution. The first is disabling behavior that has driven me crazy since the first time I ever used. By default, if a 404 or 500 error is encountered, Sitecore wants to do a 302 redirect to “/sitecore/service/notfound.aspx” or “/sitecore/service/error.aspx”. In the case of 404s, this is infuriating because now I no longer know the URL that caused error. This is easily remedied with a single setting in the Web.config:
<setting name="RequestErrors.UseServerSideRedirect" value="true" />
The actual message doesn’t change, just that the address in the browser is the original we asked for.
Now we can actually move on the to the more important part, customizing the message. If you were to dig through Sitecore’s own documentation, which best I can tell hasn’t changed since 2009, there is a white paper on 404 handling:
In that document they’re going to tell you build your own pipeline processors. That’s not the end of the world but 404 handling isn’t so unique that it should require that, in my opinion.
Going even older (2008), I found a post in the Sitecore SDN that describes a solution. If you name an item “*” in the content tree, it acts as a fallback for any items that can’t be found. Since “*” is kind of ugly for an item name, you can change it’s display name to be “Not Found”. Here’s the catch though: it only works for paths at that level. So, if I made an item with the path “/sitecore/Content/MySite/MySection/*”, it would work if someone requested “/sitecore/Content/MySite/MySection/Foo” but not “/sitecore/Content/MySite/Foo” or “/sitecore/Content/MySite/AnotherSection/Foo”. I’m sure there’s a valid application for this trick but I’m hard pressed to think of one that wouldn’t be just as simple with a site-wide 404 message. It still doesn’t address 500 errors either.
If you do some digging, there are some other options in the Web.config but I think they have some short comings:
<!-- ITEM NOT FOUND HANDLER Url of page handling 'Item not found' errors --> <setting name="ItemNotFoundUrl" value="/sitecore/service/notfound.aspx" /> <!-- ERROR HANDLER Url of page handling generic errors --> <setting name="ErrorPage" value="/sitecore/service/error.aspx" />
The problem is that, I have found, that those paths have to be physical files residing on the server, not virtual paths defined. Physical files also means they are frozen moments in time and not produced by the same renderings and templates you are using to actually build your site(s). And it’s the same physical file for all sites being served by that Sitecore instance, so it would have to be generic and universal.
Sitecore Error Manager
Luckily, someone has actually put together a drop-in module that seemed to do everything I was needing.
They have some documentation with their module but I figured I would share my experiences with it. The module installs a file at /App_Config/Include/Unic.Modules.ErrorManager.config but is ready to go with a pretty mainstream configuration. If a 404 is encountered, they attempt to use the path in the “ItemNotFoundUrl.Item”. This is a relative path from the root of your site which can be a Sitecore item, using all your existing renderings and templates. Change the path if you prefer something different.
For me, having the same path for every site is still flexible enough because those sites can choose how that item is built. Path is a small rule for a payout. 500 errors are handled by the “LayoutNotFoundUrl.Item” setting. I find it interesting that that is the only server error that is accounted for. I haven’t purposely tried to throw a different exception but so far the ones I have encountered have used this particular mechanism.
I did find one caveat, however. It would appear that all of the mechanisms for doing device detection and personalization are skipped in this situation. Therefore, only the “Default” device is ever served and any personalization on that layout will use the default settings. I suppose you should try to have as generic a design as possible for those pages and employ client-side responsive techniques if that design needs to vary between devices.