IIS7 Displays BOTH Its Own 404 Message & CF9 Messa

2019-01-19 09:26发布

问题:

My goal seems simple.

I want IIS 7.5 to handle ALL 404 File Not Found requests, whether to static or dynamic (ColdFusion 9) content, and direct users to its custom 404 HTML page.

I believe that the IIS settings I need are existingReponse=Replace, errorMode=Custom, and a file path specified for the 404. That's what I've done.

With a ColdFusion 10 install, it works. With ColdFusion 9, for some reason both the static IIS 404 response AND the ColdFusion 404 response are sent to the client and displayed. Very weird.

I've tried all manner of alternate configurations, and there seems to be some problem with each approach.

Any ideas why IIS would fail to replace ColdFusion's 404 message? Is ColdFusion failing to communicate to IIS (via the appropriate header) that it's sending a 404? Is IIS being obstinate? Why would it be different with ColdFusion 10 and ColdFusion 9?

ColdFusion 9, via CFAdmin

Global Settings
- Missing Template Handler = [no path specified]

IIS 7, via IIS Manager

Configuration Editor -> system.webServer/httpErrors

    - allowAbsolutePathWhenDelegated = false
    - defualtPath  =  [no path specified]
    - defaultResponseMode = File
    - errorMode = Custom
    - existingResponse = Replace

Configuration Editor -> system.webServer/httpErrors -> Edit Collection

    - 404 Error
        path = [DriveLetter]:\inetpub\wwwroot\CAES\global\errorHandling\404.html
        prefixLanguageFilePath = [none specified]
        respnseMode = File
        statusCode = 404
        subStatusCode = -1
    - 403 Error
        path = [DriveLetter]:\inetpub\wwwroot\CAES\global\errorHandling\403.html
        prefixLanguageFilePath = [none specified]
        respnseMode = File
        statusCode = 404
        subStatusCode = -1

回答1:

This should actually solve more than one question/problem regarding ColdFusion and IIS 7.5. On my fresh Win2k8 R2/64-bit server running CF9 Standard, this has solved the issue of dual IIS and CF error template displays, eliminated the need to set IIS to display detailed error messages to the public and returns proper error codes to the client browser.

Unless you have a specific reason to uncheck it, leave ColdFusion's Enable HTTP Status Codes box checked in the CF Admin's Settings screen so CF can properly return status codes to IIS (some 404 solutions work by unchecking this box at the expense of losing those useful headers).

Step 1:
Configure a Missing Template Handler in CF Administrator. Mine is global to the server and kept in the ColdFusion webroot - which is separate from the IIS web root and whose default location is c:\ColdFusion9\wwwroot. This global template is 404handler.cfm and contains the following simple code, which you can expand upon:

<h1>404</h1>
<p>Page Not Found</p>
<cfheader
    statuscode="404"
    statustext="Not Found">

At this point, visit your web site and execute a bad ColdFusion url: http://[domain]/bogus.cfm. You will see both the IIS remote error screen/banner followed by your ColdFusion error screen. Check the header and it is a 404. This next step will solve the dual display problem.

Step 2:
Create a local 404 handler somewhere under the web root of an individual web site. I named this file 'local404.cfm'. It consists of the following:

<h1>404</h1>
<p>Page Not Found (local)</p>
<!--- 
demonstrate ColdFusion is functional 
with some simple output 
--->
<cfoutput>
<p>#now()#<br>#cgi.server_name#</p>
</cfoutput>
<cfheader
    statuscode="404"
    statustext="Not Found">

Go to IIS Manager and click on your individual web site. Click on Error Pages and edit this site's 404 handler. Set it to execute a url and make the url '/local404.cfm' (or whatever path is appropriate). Save your work. On the right side of the screen, click Edit Feature Settings and make sure that it is set for 'Detailed Errors For Local Requests and Custom Error Pages for Remote Requests'. Next, while still in IIS visit Handler Mappings and ensure that your ColdFusion handlers are set for a Path Type of 'File' (this step may not be necessary to solve this issue, but is per ColdFusion Lockdown guidelines).

Execute a bad ColdFusion url again: http://[domain]/bogus.cfm. This time you see only the ColdFusion error screen. Check the header and it is a 404. Do the same for a non-CF allowed URL such as http://[domain]/bogus.htm and a not-allowed one like http://[domain]/web.config. In all cases you should see the local error template executing, and the proper header of 404 being issued.

What is happening? Only the local IIS-based Coldfusion-driven 404 template is executing... As soon as you enable a local ColdFusion template in IIS, the global ColdFusion template is effectively and completely disabled. However it can be replaced as shown.

Apply the above to all web sites on the server, and you can delete your global CF missing template handler. A single global template would be preferable, but this method solves the problem and gives back complete functionality without creating security or SEO issues.



回答2:

Update 2/17/2015 - Adobe has confirmed my suspicions on their own blog, http://blogs.coldfusion.com/post.cfm/onmissingtemplate


I have encountered the same situation using ColdFusion 9 and IIS 7.5. I was waiting to answer in hopes that someone else might share their configuration and also because I am not too happy with the "resolution" that I came up with. I will swallow my pride and post what I did for this situation. Feel free to comment.

First my findings. It has been a while since I worked on this but here is what I recall. In the ColdFusion administrator, under Server Settings - Settings, if you check the Enable HTTP status codes option that is what causes this situation. This setting causes the ColdFusion server to return status codes, like 404 (and others) along with it's response. So when IIS receives that 404 status after the ColdFusion processing it also processes it's 404 error handler. With the Enable HTTP status codes option unchecked then ColdFusion will send a 200 OK status for ALL errors (including 500 errors). When IIS receives the 200 status it assumes all is okay and does not process it's error handlers. This may be what some people want; having ColdFusion process it's own errors and IIS it's own - separately. Not returning an actual 404 status code however can be detrimental to search engine indexing.

Like you, that is not what I wanted. I wanted both IIS and ColdFusion to share the same 404 handler. Unlike you, I wanted all 404 errors to be handled by ColdFusion not a static HTML page. This allows me to do several things. When pages move or get renamed I can handle that with the ColdFusion 404 handler and redirect the request accordingly. Even changing the status code which is beneficial for search engine indexing (301 and/or 302 codes). I can also create short-cuts if I wish that don't really exist, log the errors, email errors, etc.

So in my configuration I have IIS's 404 errors pointing to my ColdFusion page. In the ColdFusion administrator I have the Missing Template Handler pointing to the same ColdFusion page and I have the Enable HTTP status codes option enabled (checked). With this setup the 404 page gets called twice for all non-existent .cfm templates (from both the IIS and CF handlers) but it only gets called once for every other file type (from the IIS handler only).

I never did find a way to avoid the duplicate 404 handling for the non-existent .cfm templates so I added code to my ColdFusion page. (Note that I can do this because the 404 handler is a ColdFusion page, not a static HTML page.) I just needed a way to differentiate when the page was being called from the IIS handler versus the ColdFusion handler. What I found is that whenever the IIS 404 handler calls the ColdFusion page the CGI.SCRIPT_NAME variable is the file name of my ColdFusion page no matter what page is actually requested. (IIS appends the requested URL to that in the query string, prepended with 404;) When the ColdFusion missing template handler calls the page the CGI.SCRIPT_NAME variable contains the actual file name of the ColdFusion template that was requested.

Example URL from IIS 404 handler:

http://www.yourdomain.com/404handler.cfm?404;http://www.yourdomain.com:80/non-existent-file.cfm

Example URL form ColdFusion missing template handler:

http://www.yourdomain.com/non-existent-file.cfm

Knowing this I can now include some code in my ColdFusion handler to basically ignore one of the requests. I choose to ignore the ColdFusion request because they are only for .cfm templates. The IIS requests will be for all files. So at the top of my ColdFusion 404 page I have code like this (It's not really pretty but it seems to work for me):

<cfif CGI.SCRIPT_NAME NEQ "/404handler.cfm">
    <!--- Do some processing here if you want --->
    <!--- Basically we will ignore this request from the ColdFusion handler --->
<cfelse>
    <!--- This request is from the IIS handler --->
    <!--- All non-existent file types will come through here --->
    <!--- Do all of your processing here --->
</cfif>

Another thing that I have noticed with this. When the request comes through the ColdFusion missing template handler it does not follow the normal request life cycle. I.E. The request does not call the Application.cfm file. So you need to be careful if you use Application variables in the ColdFusion page that you have setup for the missing template handler. (You can always just cfinclude the Application.cfm file for these requests.) When the request comes through the IIS 404 handler it does process the Application.cfm file as it normally would. I guess because IIS is making a request of the .cfm file.

When using the Application.cfc's onMissingTemplate function the ColdFusion administrator's setting is bypassed altogether in favor of the function. When this function is fired it also does not flow through the normal request life cycle. You should also check for the existence of Application variables within this function (if you are using any in the function) as that has bitten me before.