This was something that I meant to write about a
while ago but never got around to it. I recieved a question recently by
email and wanted to provide some tips and tricks when using ISAPI
Rewrite in a multi-tiered load balanced environment. I wrote an article
with Frank DeRienzo several years ago about using a multi-tiered load balanced enviroment. I followed this up later with a blog entry on my site for Using ISAPI Rewrite with ColdFusion.
If you are using ISAPI Rewrite to proxy requests from your webserver
(IIS) to you ColdFusion server or through a load balancer you will run
into issues when accessing various CGI variables. When proxying the
ColdFusion server sees the web server as the end client and therefore
variables like CGI.HTTP_HOST are set to the IP or hostname of the
webserver. To get around this I inject custom headers with ISAPI
rewrite, parse them in CF and then stored them in the request scope.
First I start by adding the headers to the main httpd.ini
Start->All Programs->Helicon->ISAPI_Rewrite->httpd.ini (you
may need to change the read only flag in order to save the file)
#Due to the proxy we add these headers to use in cf through
#GetHttpRequestData().headers.HEADERNAME
RewriteCond %REMOTE_ADDR (.*)
RewriteHeader CLIENT_REMOTE_ADDR: ^$ $1
RewriteCond %SCRIPT_NAME (.*)
RewriteHeader CLIENT_SCRIPT_NAME: ^$ $1
RewriteCond %SERVER_NAME (.*)
RewriteHeader CLIENT_SERVER_NAME: ^$ $1
RewriteCond %SERVER_PORT (.*)
RewriteHeader CLIENT_SERVER_PORT: ^$ $1
RewriteCond %HTTP_HOST (.*)
RewriteHeader CLIENT_HTTP_HOST: ^$ $1
RewriteCond %SERVER_PROTOCOL (.*)
RewriteHeader CLIENT_SERVER_PROTOCOL: ^$ $1
#stores the original URL before any rewriting in a custom header
RewriteCond URL (.*)
RewriteHeader x_rewrite_url_original: ^$ $1
RewriteCond X-Rewrite-Proxy (.*)
RewriteHeader x_rewrite_proxy: ^$ $1
I then create a page in CF called httpheaders.cfm and include it
early on in my CF code within Appplication.cfm or within onRequest of
Application.cfc
<cfsilent>
<!--- this page is added to handle the custom headers we write with
ISAPI Rewrite due to the issues we see with proxying requests.
--->
<cfset httpheaders=GetHttpRequestData().headers>
<cfset REQUEST.proxyheaders=structNew()>
<cfif isdefined("httpheaders.CLIENT_REMOTE_ADDR")>
<cfset REQUEST.proxyheaders.REMOTE_ADDR=httpheaders.CLIENT_REMOTE_ADDR>
<cfelse>
<cfset REQUEST.proxyheaders.REMOTE_ADDR=CGI.REMOTE_ADDR>
</cfif>
<!--- I need to find a way to handle SCRIPT_NAME better, with ISAPI
Rewrite and JRun ones are different --->
<cfif isdefined("httpheaders.CLIENT_SCRIPT_NAME")>
<!--- <cfset
REQUEST.proxyheaders.SCRIPT_NAME=httpheaders.CLIENT_SCRIPT_NAME> --->
<cfset REQUEST.proxyheaders.SCRIPT_NAME=CGI.SCRIPT_NAME>
<cfelse>
<cfset REQUEST.proxyheaders.SCRIPT_NAME=CGI.SCRIPT_NAME>
</cfif>
<cfif isdefined("httpheaders.CLIENT_SERVER_NAME")>
<cfset REQUEST.proxyheaders.SERVER_NAME=httpheaders.CLIENT_SERVER_NAME>
<cfelse>
<cfset REQUEST.proxyheaders.SERVER_NAME=CGI.SERVER_NAME>
</cfif>
<cfif isdefined("httpheaders.CLIENT_SERVER_PORT")>
<cfset REQUEST.proxyheaders.SERVER_PORT=httpheaders.CLIENT_SERVER_PORT>
<cfelse>
<cfset REQUEST.proxyheaders.SERVER_PORT=CGI.SERVER_PORT>
</cfif>
<cfif isdefined("httpheaders.CLIENT_SERVER_PROTOCOL")>
<cfset
REQUEST.proxyheaders.SERVER_PROTOCOL=httpheaders.CLIENT_SERVER_PROTOCOL>
<cfelse>
<cfset REQUEST.proxyheaders.SERVER_PROTOCOL=CGI.SERVER_PROTOCOL>
</cfif>
<cfif isdefined("httpheaders.CLIENT_HTTP_HOST")>
<cfset REQUEST.proxyheaders.HTTP_HOST=httpheaders.CLIENT_HTTP_HOST>
<cfelse>
<cfset REQUEST.proxyheaders.HTTP_HOST=CGI.HTTP_HOST>
</cfif>
</cfsilent>
After including this code you can access the client's IP with "REQUEST.proxyheaders.REMOTE_ADDR".
One other challenge is detecting if a user is using HTTPS if you
have front ended your site with a load balancer and use the SSL
accelerator of BIG-IP or Cisco CSS. To the app it looks exactlyt he
same. I will save that discussion for another blog posting. Let me know
if you have any questions.
There should be a similar solution with mod_proxy in apache by
adding headers I just have not researched it. If you have the
information post your findings and I will add it to the blog.
Hi Brandon, Your articles have been very helpful. A couple of questions: 1. What is the issue with using the JRun connector with a BigIP hardware load balancer between the web server and the application servers? Is it still problematic using the latest version of ColdFusion? (We are having problems, so I assume it is, but I was wondering about the root of the issue) 2. We are using the multiple server install of ColdFusion MX 7 Enterprise (running on JRun) in distributed mode (separate machines for web and application servers) Can the ISAPI rewrite solution you recommend be used if we are using a multi-homed web server? Our sites do not use a context root, and different web sites point to different home directories. This works fine when we use the JRun connector to point to a single ColdFusion server (and we don't even have to have a web server installed on the application server box), but we run into issues when trying to load balance using the F5 BigIP solution. I have installed ISAPI rewrite, but it seems like this solution is going to require that the communication between the F5 and the CF server be in HTTP:, and I don't know that the site of origin on the multi-homed web server is passed. Even if it is, the administrative overhead of managing the sites on the application servers makes it an unattractive solution. Any suggestions you might have would be appreciated! -- Jim
<p> Sorry for the delay in responding. My email box is a mess of spam these days since I exposed it via my blog. I also fixed the issue with comments on the website. I have added this response to the blog posting as well. </p> <p> 1. It has been a while since I last worked with this but this is what I remember. JRun uses JRPP which is a proprietary protocol between the connector and JRun. It opens a pool of persistent connections and forwards requests through this pool. The hardware load balancers work best with HTTP although they do support many other protocols. I think the fact that the connection is persistent throws it off and requests keep getting sent to the main machine 2. Multi-homed sites do not work with this configuration unfortunately. Each one would need its own instance and unique webroot on the front end. </p> <p> Thanks Brandon </p>
In the post above you say: "One other challenge is detecting if a user is using HTTPS if you have front ended your site with a load balancer and use the SSL accelerator of BIG-IP or Cisco CSS. To the app it looks exactlyt he same. I will save that discussion for another blog posting.".
Can you give me some insight into how you handle this?
Great post about the ISAPI rewrite and CGI variable issues. We have our clients coming in through ssl and was wondering if you have anymore insights? CGI.HTTPS is listed as a cgi variable in CF as well, could this be mapped in a similar way?
Something like: RewriteCond %HTTPS (.*) RewriteHeader CLIENT_HTTPS: ^$ $1
Hey Brandon, did you get around to writing a post about the HTTPS issue? We're constantly struggling with this issue as well.
Tony