Technical Article IIS X-Forward-For ISAPI Filter September 23, 2005 by Joe Pruitt 4373 article us 2 A recent customer issue came up where they were load balancing servers but we unable to get the true client address logged in their IIS logs. They had their servers fronted by a BIG-IP and when clients would make requests the address passed to the server was the internal address of the BIG-IP and not that of the client. This is a common issue with proxies and fortunately there is a standard for forwarding client information. It is the HTTP X-Forwarded-For header which is handled by most proxies. So, I set out to find an existing ISAPI filter to replace the c-ip (client ip) log value in IIS with the contents of the X-Forwarded-For header (if it exists). I was amazed to find that I couldn't find a single instance of any open source (or even commercial) filter that would do this. So, I dug out Visual Studio and whipped up a filter that does just that. It's very basic and contains no user configuration so all you need to do is plug it into your Web Applications list of ISAPI Filters within the IIS Administration and you're set to go. We've released the source under the iControl End User License Agreement (available in any iControl SDK download). You can download it in the CodeShare section of DevCentral. If you find a way to optimize this filter, please let me know and I'll update the sources here. After 24-hours of posting, a customer already returned some performance testing on the filter indicating that it only effected the traffic by less than 1 percent. I'm sure there are ways to optimize the memory allocation in the filter to speed this up a bit more, but I'll leave that for the community to work on. Oh, and it should be noted that the X-Forwarded-For header isn't supported the same way across all proxy products so you'll want to make sure you test this out before using it. It is expecting the header to only contain an IP Address as it does a straight substitution on the value in the c-ip section of the log entry. Enjoy! -Joe [Listening to: Letterbomb - Green Day - American Idiot (03:48)] last modified: March 06, 2007 33 Comment(s): -1 Evidently this can now be done natively in IIS 7+ (WinSvr2008+) without requiring an ISAPI filter.In the article at LoadBalancer.org (http://blog.loadbalancer.org/iis-and-x-forwarded-for-header/) Rob Cooper discusses how you can now use "IIS Advanced Logging" installable feature to capture custom headers like X-Forwarded-For in the logs. 0 Unfortunately, I can't test that configuration right now. I've released the source for both the ISAPI filter and HTTP module if anyone can take a look and try to figure it out. When I get some free time in a couple of weeks, I'll dig in and see if I can reproduce it.-Joe 0 If you are running on IIS7+ the I'd suggest you look at my native HTTP module.devcentral.f5.com/.../...iis7-source-included.aspxAlthough, if you are stuck on ISAPI, I have tested the ISAPI Filter on 64-bit IIS7. Not sure why IIS7.5 wouldn't work.-Joe 0 @Clive - Glad it worked out for you! 0 I just rebuilt and tested with Win2k8-x64 and IIS7 and it works like a champ. The link in my post is updated with the latest build.-Joe 0 Instead of writing the X-Forwarded address to the log, how can you modify the remote addrr variable to get replaced with the x-forwarded ip. 0 Hope that helps out!-Joe 0 There is no newer version as I haven't looked at this for years. The source should still be available if you want to recompile for yourself. The most likely issue of the dll not being able to be loaded is an issue with permissions. Select Properties on the .dll from explorer and give full control to all and see if it then loads.-Joe 0 I haven't looked at this for a few years now so I'm not sure I can help much (despite originally writing it). I'll try to get some time to install it on one of my dev systems and see if I can replicate the issue.-Joe 0 all I see in the event log is The HTTP Filter DLL C:\F5XForwardedFor\F5XForwardedFor2005\Release\F5XForwardedFor.dll failed to load. The data is the error.I tried giving the IUSR user, the IIS_WPG, IIS Process account, and network service account permissions and it still has the downward red arrow. 0 That could be a lot of things. First, I would go to the directory where the dll is located with explorer and make sure that the IIS user has access to the file.Otherwise, you can look into the evert viewer to see if there is information about the load error in there.-Joe 0 All I get is a red Arrow pointing down and a status of unknown under IIS 6 32 bit. 0 It's been quite a while since I posted that source and can't find the loading of the INI file in it. I'll have to dig through my dev machine to see if I still have it. I'll let you know.-Joe 0 Thanks for the tip Eddie. That header should be available to any web apps that have access to the HTTP request headers. IIS likes to prefix "HTTP_" ahead of all the HTTP header names to distinguish them from the ones that it inserts. For Apache, you'll like just access the "X-Forwarded-For" header directly.-Joe 0 Great, Glad it helped you out. The HTTP profile will be the best performing option but there are cases where you may want to do this conditionally. If so, then you're best bet is in an iRule.-Joe 0 Joe, Thank You! This is exactly what I needed.I am curious, is there any advantage/disadvantage to using this in an iRule vs. an HTTP Profile (SOL4816).Thanks,Jason 0 Joni, thanks for the link! I'm sure others out there will appreciate it.-Joe 0 As far as I can tell it does, but I have no way to test it.-Joe 0 Right now, all it does is look for an incoming X-Forwarded-For HTTP header. If it's found, the value in it is used for the c-ip value in the IIS access logs. I'm sure you could write a filter that does what you propose, or take the source from the XFF filter and tailor it to your needs, but this one doesn't do what you are asking.-Joe 0 No issues that I know about. I would just make sure that the security settings on the dll are set so that IIS is able to load it properly.-Joe 0 Greetings-Any known issues for this filter related to IIS 6? I can't get the filter to load in IIS 6 worker process isolation mode. IIS 5 compatibility mode seems to work fine. Thanks!! 0 The filter will take the entire string and replace it in the logs. The source is available if you want to tweak the implementation.Glad you like it!-Joe 0 The download zip file contains 32bit and 64bit versions.-Joe 0 This filter does not do anything with the headers. It looks for a header (X-Forwarded-For by default, but overridable by a ini file). If that header is found, it replaces the contents of the IIS C-IP value with the contents of that header.It does not add or remove headers so any HTTP header that is passed into it, will be passed on to the web application. There is no rewriting needed as this is just a filter. It just sniffs whats coming in and passes it through to the backend.So, if a X-Forwarded-For header is passed it to the filter, it will be passed out as well. Are you seeing different behvavior? The source is available for download as well if you want to look for yourself at what the filter does.-Joe 0 Thanks for the link Mike. I believe the code in that blog post is very similar to my filter. I've also got support for an ini-file override of the header name. I think I've got David's post by 5 days though B-).-Joe 0 Kazu, can you post your code changes or send them to me and I'll get them integrated into the download?Thanks!-Joe 0 It's nothing that complicated. All it does is read the X-Forwarded-For HTTP header sent to the webserver from the firewall (or other intermediary) and replaces the c-ip field in the IIS logs with that value for that request. Otherwise, there is no other way with IIS to show the true identity of a client if a firewall changes the source ip address.-Joe 0 Not sure what's different with a SSL configured server. As far as I'm aware, the SSL decryption is performed before the content gets to the ISAPI filters. I see no reason why one would work and the other wouldn't. 0 Glad you like it! As far as I know there is no way to add headers to the IIS log facility. So, this filter will replace the C-IP value with the contents of the X-Forwarded-For header (if it exists). 0 If the status is Unknown, that means that you haven't sent any traffic through it. Try accessing one of the pages on that website and the status will either be an up arrow (good) or a red arrow (bad) but shouldn't be Unknown. Also, I don't believe you need to create a new application. All I did was to add it to the filters list within an existing website. -Joe 0 Thanks. The only thing I really need from you is your contact information? You can send it to me privately by clicking on the "Contact Me" link on the top left of my blog. Then I can get you the updated filter. -Joe 0 Good points - I'm sold. I'll incorporate the check for internal networks and bypass replacement of the X-Forwarded-For header if the source address is from the ranges specified above. Since I can't really test this on my setup, if anyone is willing to test out my code changes, I'll be able to post them for everyone to share. Oh, and I've made a few more optimizations in the code for the cases when X-Forwarded-For headers aren't passed in. -Joe 0 John, Currently the code does a straight replacement IF the X-Forwarded-For header exists. I guess I'm not clear on why internal networks are different than external ones. In your case doesn't the internal proxy add the X-Forwarded-For header with the true client address? If so, then this filter will replace the address. The real problem is that the proxy is hiding the original source address and throwing in it's own. So there is no way for the filter to know what the true address is unless the proxy is passing it through directly. In which case you don't need the X-Forwarded-For header. Am I missing something? I'd be glad to make modifications to the filter if it makes sense to a wide audience. The C++ code is very simple... -Joe You must be logged in to post comments.