In this article we will see how to create a custom 404 Response error page in ASP.NET, which will:
- produce a friendly output to the user
- send an email to the webmaster letting them know about the broken link so they can fix it.
The default 404 page
When we link to a URL that does not exist, the web server sends back a 404 Response. This basically tells us that the requested resource does not exist. Microsoft’s Internet Information Server (IIS) is set up to send a default HTML page when it encounters this kind of error. This page, along with others making up the default server errors, are created upon installation of IIS. You have probably seen it many times while browsing:
This output is simply an HTML file which usually resides (depending on your installation) in C:\WINNT\help\iisHelp\common\404b.htm.
Why should we change the default page?
A quick look at the screen capture above should convince you right away that this is not the most user-friendly page. In fact, a user who sees this will probably abandon your site. There is no way for them to know what happened. Did the page move? What site is this? What are my options now?
Since this is a standard HTML page, we can create our own and tell IIS to send that instead. That way, we can have a friendlier page for the user, one that has our logo for example, the same look and feel as the rest of the site, and some options of what to do now that the resource was not found. But most important of all, we can add our custom ASP code in it to perform some cool stuff, like capturing the broken link or the refering page, and then store that in a database or send an email to the webmaster. You might even want to perform a search in the background based on the URL, and present the user with some more options.
The main goal is to not let the user go away. Getting a user to come to your site is hard enough. Presenting them with a friendlier experience, even when some resource is not found, is certainly a step in the right direction in motivating them to click around.
Changing the default page in IIS
Go to Start > Programs > Administrative Tools > Internet Services Manager. Find the website you want to change in the list of sites, right click on it and select Properties. Go the the Custom Errors tab.
Here is where you can change all the server errors that are displayed. Scroll down to the 404 HTTP Error, select it by clicking on it, and then click on Edit Properties….
The path to the file on your machine can vary depending on how IIS was installed. You can change the file in 2 ways:
- as a File, by browsing the file system and selecting the file.
- as a URL, by typing the virtual URL of the file.
What’s the difference? You can run ASP code only if you choose a virtual file, since that is simply redirecting to a file on your site. If you have ASP.NET installed you can run .aspx files. So in our case, we will change this to a virtual file. Our custom ASPX file, which we will see below, resides at /servererrors/404.aspx. You can name it whatever you want and place it anywhere you want on your site. Therefore:
Change the Message Type to URL, and type in the URL the virtual URL of the file on your site to redirect to. Click OK all the way through to save your changes.
Editing the web.config file
When someone now links to a broken link on our site, they will see this instead:
However, while this will work for almost all files extensions (.htm/.html/.asp etc.), it will not work for ASPX files. The reason is that those files pass through a special parser which instead outputs a .NET error. To get the .NET application to redirect to this custom 404 file, we need to edit our web.config file in the root of the site. Open up (if you do not have one already then create it) the web.config file in the root of your site and type this in it:
<configuration> <system.web> <customErrors mode="On"> <error statusCode="404" redirect="/servererrors/404.aspx" /> </customErrors> </system.web> </configuration>
Here we are telling the .NET application to turn on custom errors, and for each Response Code (like 404) we can create a separate node and redirect to a specific virtual page.
The code in 404.aspx
<%@ Page Language="VB" runat="server" explicit="true" strict="true" %> <%@ Register TagPrefix="UserControl" TagName="Header" Src="head.ascx" %> <%@ Register TagPrefix="UserControl" TagName="Footer" Src="tail.ascx" %> <%@ Import Namespace="System.Web.Mail" %> ...
Notice that I am registering 2 UserControls to include my header and footer. You can do something similar if you want, or take them out. They are not necessary for the code to run. I import the System.Web.Mail namespace so we can send an email to our webmaster.
... <script language="vb" runat="server"> Sub Page_Load() '-- Get Query from the URL Dim strQuery As String = Request.ServerVariables("QUERY_STRING") '-- avoid running our code if someone browses ' the 404.aspx file directly If strQuery <> "" Then '-- Page with the broken link Dim strReferer As String = Request.ServerVariables("HTTP_REFERER") '-- remove the default appended string to the url strQuery = strQuery.Replace("404;http://", "") '-- Find the position of the root "/" in the string Dim intSlashPosition As Integer = strQuery.IndexOf("/") '-- Remove all text before the "/" strQuery = strQuery.Remove(0, intSlashPosition) '-- set the label control's text lblQueryString.Text = strQuery ...
When the page loads, we can get the broken link from the URL. When a redirect to the 404 page happens, the server appends the broken link to the 404 page:
In the case above, the broken link was /articles/list.aspx. We can get that through the Request.ServerVariables(“QUERY_STRING”). If the query is empty, then it probably means that the user found this ASPX page on our site and simply browsed to it (although highly unlikely). Nevertheless, in that case we do not want to run our code. That’s why all the code is nested inside the condition that the strQuery is not empty.
... '-- Send Email to the Webmaster Dim objMail As New MailMessage() Dim strEmailBody As String strEmailBody = "Xefteri - 404 Page Not Found : " & DateTime.Now & "<br>" & "------------------------------------------------------" & "<br>" & "<br>" If strReferer = "" Then strEmailBody = strEmailBody & "Someone typed in this broken link directly:" & "<br>" Else strEmailBody = strEmailBody & "The page <a href=""" & strReferer & """>" & strReferer & "</a> contains a broken link:" & "<br>" End If strEmailBody = strEmailBody & "<br>" & strQuery '-- change the FROM and TO emails for your site objMail.From = "email@example.com" objMail.To = "firstname.lastname@example.org" objMail.Subject = "Xefteri - 404 Page Not Found" objMail.Priority = MailPriority.High objMail.BodyFormat = MailFormat.Html objMail.Body = strEmailBody SmtpMail.SmtpServer = "" SmtpMail.Send(objMail) '-- strQuery = "" Else '-- switch panel displays to display the error ' when a user simply navigates to this file pnlValid404.Visible = False pnlNotValid404.Visible = True End If End Sub </script> ...
Next, we send an email to the webmaster of the site, letting them know about the broken link and where it came from so they can take a look at it. In my HTML code I use the .NET Panel Controls to switch content. This is similar to the classic ASP where you would nest your output within Subs and depending on some condition you would display one and not the other. Panels make this easier. You can see how in the HTML code below.
... <html> <head> <meta name="robots" content="noindex,nofollow"> <title>Xefteri - 404 Page Not Found</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> </head> <body bgcolor="#FFFFFF"> <UserControl:Header runat="server" /> <h2>Page Not Found (404 Error)</h2> <asp:panel id="pnlValid404" runat="server"> <p><asp:label id="lblQueryString" runat="server" ForeColor="#FF0000" /> could not be located.<p> <p>An email has been sent to the webmaster about this problem, and action will be taken to fix it.</p> </asp:panel> <asp:panel id="pnlNotValid404" runat="server" Visible="False"> <p>This page works only as a redirect when an ASPX page is not found.</p> </asp:panel> <UserControl:Footer id="myFooter" runat="server" /> </body> </html>
The UserControl Header and Footer are my include files for simply the header and footer that go on every page. The label control with ID lblQueryString outputs the strQuery from our code. And finally, the two panels switch content depending on what we want to show to the user. The pnlNotValid404 is set to not visible be default since this is our error display, and we only set it to visible if one occurs.
Some hosting companies will not repoint the default 404 Response page to your custom code. Some will do it for a fee. However, if your site is made up of only ASPX files, then by simply copying the web.config file in the root folder of your site will actually make this work. There is no need to edit the IIS manually. Keep in mind though, that this will only work for .aspx extensions, unless you also change the IIS.