We have a server setup as follows:
- Windows 2012 R2
- Coldfusion 10, Enterprise
- IIS, configure with custom 404 page (execute on server)
The custom 404 page (a CFM file) handles missing URLs and checks them against custom URLs in the database. If it finds a match, it will display the relevant data. The problem occurs when the custom URL maps to a non-existant CFM file; for example.
/home/map.cfm (not a real file or directory)
Now, when a user requests this URL, the server sees that it is a CFM file and correctly passes it to ColdFusion (via the ISAPI redirect). Tomcat sees that this file does not actually exist, and returns a 404. IIS sees this 404 message and executes the custom error page (/errors/404.cfm).
The resulting CGI variables do not infact allow us to retrieve the original URL (to map it to a virtual URL in the database), usually provided as part of the CGI.QUERY_STRING variable. Instead, the QUERY_STRING variable contains the path to the 'isapi_rewrite.dll' file, in the 'jakarta' directory.
Is there any way to retain the originally requested URL (of a CFM file), after Tomcat has returned a 404 error for said page?
Solution, put together by myself, inspired by Thomas Gorgolione
As suspected, the issue invariably came down to the request being forwarded to the ISAPI_REDIRECT module, even if the file in question did not exist.
As Thomas suggested, an elegant solution is to utilize rewrites to check that the file / directory actually exists, before passing it through to the ISAPI_REDIRECT module.
While the software Thomas suggested would of done the trick, I was unable to get it to install on a 64bit Windows 2012 R2 machine. I've managed however to fall back to utilize the URL Rewrite module included with IIS8 (and possibly earlier versions).
See my web.config entry below that provides a solution.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpErrors>
<remove statusCode="404" subStatusCode="-1" />
</httpErrors>
<rewrite>
<rules>
<rule name="404 HTTP">
<match url="(.*)" />
<conditions>
<add input="{REQUEST_URI}" pattern="CFFileServlet/(.*)" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{SERVER_PORT_SECURE}" pattern="0" />
</conditions>
<action type="Rewrite" url="/404.cfm?404;http://{HTTP_HOST}:{SERVER_PORT}{REQUEST_URI}" />
</rule>
<rule name="404 HTTPS">
<match url="(.*)" />
<conditions>
<add input="{REQUEST_URI}" pattern="CFFileServlet/(.*)" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{SERVER_PORT_SECURE}" pattern="1" />
</conditions>
<action type="Rewrite" url="/404.cfm?404;https://{HTTP_HOST}:{SERVER_PORT}{REQUEST_URI}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
The above configuration file takes into account both HTTP and HTTPS requests. It will also take into account the the CFFileServlet directory, which ColdFusion uses to host temporary images and assets. If the URL starts with this directory, it will be passed on as a normal request.
Thanks for your input everyone.
See Question&Answers more detail:
os