Using ColdFusion to Handle 404 Errors (Page Not Found) On Development Server

Posted June 29, 2006 at 8:50 AM by Ben Nadel

Tags: ColdFusion

Handling 404 errors can be tricky no matter what. On your live, production server, you can set up custom 404 handling on a site-by-site basis via IIS custom error templates. However, for many of us, this is trickier on the development server where there are no site. Its just one web server with many sub directories, none of which are usually virtual sites.

To remedy this situation, I have developed a 404 error handler that is defined at the root of the developmental web server. This 404 error handler catches 404 errors and attempts to forward the errors to individual "site" error handlers in an attempt to mimic 404 error handling on a live site. This setup makes a few assumptions:

  1. Your 404 errors are handled by a ColdFusion template named "cferror.cfm"
  2. Your cferror.cfm template is located either in the root of the site (a sub directory on the development server) or at least that it is in the same directory or a parent directory of the requested non-existent page.

The system works by catching the 404 error at the root of the ColdFusion web server. It then takes the requested page and starts crawling up the directories url. For each directory, it check for the existence of the cferror.cfm template. If it is found, the root 404 error handler performs a CFLocation to that cferror.cfm template, sending, as the query string, the same "404;" query string that IIS has defined.

As the system crawls up the directory path, after checking for cferror.cfm, it checks for either an Application.cfm or Application.cfc file. If it hits one of these files, it stops crawling and displays its own Page Not Found message. The idea here is that we don't want to leave the context of the application when search for a 404 error handler.

I have prevented infinite loops (the root 404 error handler constantly CFLocating to itself) by stopping the search once there is no more directory to crawl (signaling that we are in the root directory). I have not tested this, but I assume this means that if the development server's cferror.cfm template is NOT IN THE ROOT of the web server, the infinite looping IS possible.

A word of caution: This template is done to help develop 404 error handling by simulating 404 error handling on the live, production server. It DOES NOT MIMIC live 404 error handling. On the live server, IIS will pass control directly to your site's 404 error handler without doing any URL location changes. Therefore, from your BROWSER's PERSPECTIVE, on the live server, your missing page URL may be in a different directory as your 404 error handler. On the development server, however, the missing page URL is ALWAYS in the same directory as the 404 error handler.

For example, let's say you request a non-existent page "ben-nadel/is/cool.htm" on the live server.

Browser sees:
ben-nadel/is/cool.htm

ColdFusion sees:
cferror.cfm

The web browser is TWO directories down in the site structure. The relative paths would start with "../../".

If you do the same thing on the development server:

Browser sees:
cferror.cfm?404;http://..../ben-nadel/is/cool.htm

ColdFusion sees:
cferror.cfm

Both the web browser and the 404 error handler are in the SAME directory (due to the root 404 error handler's CFLocation). The relative paths would be empty since there is no sub-folder URL.

The cferror.cfm template as it exists in the root of my development server:

  • <!---
  • Set a short timeout since this page should not
  • hog any resources.
  • --->
  • <cfsetting requesttimeout="5" />

  • <!--- Get the query string. --->
  • <cfset strQueryString = CGI.query_string />

  • <!--- Check to make sure that we have a 404 error. --->
  • <cfif Find( "404;", strQueryString )>

  • <!--- Strip out server name. --->
  • <cfset strDirectoryPath = ReplaceNoCase( strQueryString, CGI.server_name, "", "ONE" ) />

  • <!--- Strip out 404 and protocols. --->
  • <cfset strDirectoryPath = REReplaceNoCase( strDirectoryPath, "(404;)|[a-z]{2,5}://", "", "ALL" ) />

  • <!--- Remove any query strings. --->
  • <cfset strDirectoryPath = REReplace( strDirectoryPath, "(\?.*)$", "", "ONE" ) />


  • <!---
  • If there is a file being used, get the directory
  • from teh path.
  • --->
  • <cfif REFind( "\.[\w]*$", strDirectoryPath )>

  • <!--- Remove the file name. --->
  • <cfset strDirectoryPath = GetDirectoryFromPath( strDirectoryPath ) />

  • </cfif>


  • <!---
  • Set the initial current directory value that will
  • be check during the loop.
  • --->
  • <cfset strCurrentDirectory = strDirectoryPath />


  • <!---
  • Loop over the directory path while we still have
  • directories and look for the cferror.cfm.
  • --->
  • <cfloop condition="true">

  • <!--- Get the directory from the path. --->
  • <cfset strCurrentDirectory = GetDirectoryFromPath( strCurrentDirectory ) />

  • <!---
  • Check to see if we need to break. We don't
  • want to find THIS cferror.cfm page as that
  • will just cause a crazy infinite loop. At this
  • point, just let the page finish to show the
  • page not found error.
  • --->
  • <cfif (Len( strCurrentDirectory ) LTE 1)>
  • <cfbreak />
  • </cfif>


  • <!--- Check to see if the cferror.cfm file exists. --->
  • <cfif FileExists( ExpandPath( strCurrentDirectory & "cferror.cfm" ) )>

  • <!---
  • We found an error template, so relocate to
  • that template. To make sure our paths are not
  • crazy, lets cflocation to it and use the same
  • query string that we got.

  • CAUTION: This is not how the LIVE server will
  • handle this action. This is designed for local
  • error handling on the developmental serer.
  • --->
  • <cflocation url="#strCurrentDirectory#cferror.cfm?#CGI.query_string#" addtoken="no" />
  • <cfbreak />


  • <!--- Check to see if we can find an application file. --->
  • <cfelseif (
  • FileExists( ExpandPath( strCurrentDirectory & "Application.cfm" ) ) OR
  • FileExists( ExpandPath( strCurrentDirectory & "Application.cfc" ) )
  • )>

  • <!---
  • We didn't find a cferror.cfm page, but we did
  • hit the root of an application, so break out
  • of this loop. We don't want to crawl up the
  • directory any more than we have too. If this
  • application doesn't catch errors then just stop.
  • --->
  • <cfbreak />


  • <cfelse>

  • <!---
  • We didn't find the file or hit any application
  • roots, so remove the right most slash for next
  • loop. We do this otherwise the
  • GetDirectoryFromPath() will not be able to keep
  • moving up path.
  • --->
  • <cfset strCurrentDirectory = REReplace( strCurrentDirectory, "[\\/]{1}$", "", "ALL" ) />

  • </cfif>

  • </cfloop>

  • </cfif>


  • <!---
  • If we are still here, then we didn't find a directory
  • containing a cferror.cfm. Dump out some info to help
  • the user debug.
  • --->

  • <h2>
  • 404 Page Not Found
  • </h2>

  • <p>
  • <cfset WriteOutput( CGI.query_string ) />
  • </p>

  • <cfdump var="#CGI#" />



Reader Comments

Aug 16, 2009 at 12:33 PM // reply »
3 Comments

know this post is old but this was exactly what I was looking for. Awesome


Aug 17, 2009 at 8:22 AM // reply »
11,243 Comments

@Joshua,

Awesome. Never too late :)


Sep 2, 2009 at 10:56 PM // reply »
1 Comments

Ben, is this solution appropriate for my production server running about 80 sites? I provide a ColdFusion CMS and each site is an instance (still in their own directory on the prod server) and I am looking for global error handling. Basically want to show the friendly message to the user and email or stare the actual error to me.

Seems something would have to be done in both CF admin and IIS to cover all the bases? (that is, provide a friendly message for any error?)

Providing I wanted to govern all these sites with one solution, can/shold the cferror.cfm file(s) be placed in a common place outside each site root? Thanks for any info.

Jason


Sep 6, 2009 at 11:45 AM // reply »
11,243 Comments

@Jason,

I wouldn't use a global error handler to handle ColdFusion errors - I'd set up something like a CFError tag, or the OnError() event handler in each of the given Application.cfc files.

Now, if you're talking about a global missing template handler, then I believe that can be set up in the ColdFusion administrator, but I have not done that before (due to laziness most likely). You have to be careful in that template, however, as it does not support the full array of ColdFusion functionality.


Oct 7, 2009 at 12:48 PM // reply »
5 Comments

Ben,

Would an onMissingTemplate function in the application.cfc be different from your method?

Thanks!

~kate


Oct 31, 2009 at 5:54 PM // reply »
11,243 Comments

@Kate,

The onMissingTemplate() is good, but it only works with CFM/CFC pages; as such, it won't work with anything like .htm pages or even partial URLs that get pasted improperly.


Jul 30, 2010 at 2:14 PM // reply »
1 Comments

Fantastic!

I'm going to use this all over the place. Well, technically, I'm going to use it in only ONE place, but it's going to be a huge improvement for handling missing pages.


Aug 1, 2010 at 7:21 PM // reply »
11,243 Comments

@Leonard,

Recently, I have started using IIS Mod-Rewrite and have found it really nice. But, before that, I was using this 404-handling method with great success for like 4 years.


Feb 10, 2011 at 11:04 AM // reply »
1 Comments

Hi Ben,

I have a project where we're receiving a list of URL's from auto dealerships to display their car inventory, the problem I'm having is that some of the images don't display because of 404 errors. I'm trying to find a way to display a default placeholder if the URL is bad, but I cannot figure out how to detect the 404 error in the photo URL. Do you have any advice?

Thanks,
Justin


Sep 6, 2011 at 10:08 AM // reply »
33 Comments

Ben, where we gonna put this template , i mean how this file gets called automatically every time a non existent file is requested.


Sep 6, 2011 at 10:20 AM // reply »
33 Comments

Ben, Suppose I run a url http://localhost:8500/mytest/blaah.......

it returns me java.io.FileNotFoundException.

But I want to handle this in my customised showing custom error page.


Sep 22, 2011 at 9:53 AM // reply »
1 Comments

For Apache/Linux:

I use HostMySite, and I set up the 404 handler page. Apply something like my code below on the handler page:

<cfset the404 = cgi.REDIRECT_URL>

<cfif the404 CONTAINS ".php">
<cfset theFileName = listLen(the404,'/')>
<cfset theFileName = listGetAt(the404,theFileName,'/')>
<cflocation addtoken="no" url="/some_directory/#replaceNoCase(theFileName,'.php','.cfm')#">
</cfif>


May 17, 2012 at 11:32 PM // reply »
1 Comments

Very easy the configuration.

I read a lot pages and I can't find the solution.

I open the administrator and change this

Administrator/server settings/Error Handlers/Missing Template Handler

and put the page that you want load and it is missing.

I create a cferror.cfm but it could be a index.cfm

/cferror.cfm



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 22, 2013 at 5:35 PM
Script Tags, jQuery, And Html(), Text() And Contents()
This is still an issue 2 years later. jQuery is supposed to remediate these cross browser issues, no? I have been unable to find any statement from the jQuery team calling this behavior "by de ... read »
May 22, 2013 at 12:44 PM
Ask Ben: Query Loop Inside CFScript Tags
In cf10, if you call a function that has: local.result = {}; local.result.msg = ""; local.svc = new query(); local.svc.setSQL("SELECT * FROM..."); local.obj = local.svc.exe ... read »
May 22, 2013 at 12:29 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben: What version of Java are you using? Also, did you test users.id to see what Java reports as the data type? I wonder if it's not a Java primitive data type, but getting returned as something ... read »
May 22, 2013 at 11:47 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dana, Awesome - so it looks like this bug was fixed in ColdFusion 10. Thanks so much for double-checking that. ... read »
May 22, 2013 at 11:37 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
When I c&p and run on cf10, I get: Selected User IDs: 1,4 User 1 selected: YES - YES User 2 selected: NO - NO User 3 selected: NO - NO User 4 selected: YES - YES User 5 selected: NO - ... read »
May 22, 2013 at 11:27 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Tom, Good thought, but no dice. Both of these still exhibit the same behavior: users.id[ users.currentRow ] users[ "id" ][ users.currentRow ] It's just something whacky happening with ... read »
May 22, 2013 at 11:07 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
Could your problem be that "users.id" is actually an ARRAY, not a single value? Perhaps try it again with "users.id[1]" (I only have CF8 here at work). ... read »
May 22, 2013 at 7:52 AM
Nested Views, Routing, And Deep Linking With AngularJS
Hi, Just a quick thank you. As it happens, for my own purposes, the pending ui-router work being done in native angular is likely the one I'll adopt, but your exploration, code and documentation of ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools