IIS7/8: Logging the real client IP in the IIS hit logs
I’ve seen questions around logging the real client IP in the IIS logs come up a handful of times in the past few weeks, so I figured I’d try and tackle that here. If you’re using load balancing with IIS (who isn’t these days?), then you’ve probably seen that by default, the load balancer’s IP address is generally what’s logged as the client IP in the c-ip field in your IIS logs. These days, most load balancers (and proxy servers) should have the ability to preserve the original client IP in an optional header: X-Forwarded-For. All well and good, but how exactly do we get IIS to log that XFF value to the c-ip field?
First, why would you want to? That’s easy enough to answer: if all you’re logging in c-ip is the IP address of your load balancer or proxy, it makes it harder to distinguish between requests from different clients, which can impact troubleshooting, reporting, or even forensic analysis of the logs should any penetration attempts occur. There may be other reasons as well, but that last one alone should make anyone running a web server want to ensure they’re logging the real client IP address. Out of the box, IIS doesn’t have any built-in functionality to read the value of that header and log it as the c-ip value, so to handle it, you have a couple of options:
1. You could use Advanced Logging to handle this, but personally, I feel like this is overkill that adds more administrative overhead than you need for this purpose. Introducing Advanced Logging means that you now have two hit logs to deal with, as this module logs to a separate file (located in ‘%SystemDrive%\inetpub\logs\AdvancedLogs’ by default). If you choose to go this route, it’s not that hard to set up, but you’ll need to add a custom logging field entry to the Advanced Logging config. I’m going to steal re-use a sample that Robert McMurray posted internally a while back for this exact purpose:
<location path="Default Web Site">
<system.webServer>
<advancedLogging>
<server>
<fields>
< field id="X-Forwarded-For" sourceName="X-Forwarded-For" sourceType="RequestHeader" logHeaderName="x-forwarded-for" category="Default" description="" defaultValue="" loggingDataType="TypeLPCSTR" />
</fields>
</server>
</advancedLogging>
</system.webServer>
</location>
There are also some instructions on loadbalancer.org on how to set this up if you want to go this route: http://blog.loadbalancer.org/iis-and-x-forwarded-for-header/
2. The cleaner option, in my opinion: Use an HTTP module to handle it during request processing. If you don’t want to write one yourself, there are a couple out there that can do the job for you:
OPTION 1: ARRHelper: http://blogs.iis.net/anilr/archive/2009/03/03/client-ip-not-logged-on-content-server-when-using-arr.aspx
When I originally wrote this blog entry, my understanding was that this module wasn't supported by the product group, as it was written by one of their developers as a side project. After an internal discussion a few months back, I discovered that the product group does in fact support this module now, so for those looking for a supported option that isn't Advanced Logging, this is it (big shout-out to the IIS PG for letting me add that info :) ). I’ve seen it used for this purpose (without ARR in play) in a couple of fairly large, high-traffic environments without any issues or performance degradation. Note: It does have one behavioral issue that I should mention, which is by design. In addition to logging the real client IP address in the c-ip field, it will also change the s-port value to either 80 or 443 depending on whether HTTP or HTTPS was in use for the request. Not an issue if your site is running on the standard HTTP/HTTPS ports, but if any of your sites run on non-standard ports, the s-port value won’t reflect this in the IIS logs when ARRHelper is in use. One example of what I mean: Let’s say you host www.contoso.com, and in the IIS bindings for your site, the HTTP binding is set to a port value of 8080, and you're load balancer handles redirection of client requests to the site on port 80 back to 8080 on the web server. In the IIS logs, with ARR in place, the s-port value would show as 80 in the IIS logs, even though the site is bound to and listening on port 8080.
Again, this is expected behavior. This is done to ensure that any links created by the code on the sever are relative to the port on the ARR/load balancer side (per the developer of ARRHelper). It shouldn’t be a big deal, however, unless you’re relying on the s-port value to differentiate your sites when you mine your hit logs.
OPTION 2: A 3rd party module, written by one of F5’s architects: https://devcentral.f5.com/blogs/us/x-forwarded-for-http-module-for-iis7-source-included#.UdrYQ6TD9mM
Much like ARRHelper, it doesn’t look like F5 provides official support for it (per Joe’s last sentence), but it appears he also provides the source code for it for those who want to see how this work is done in an HTTP module. Disclaimer: I’ve never downloaded/used this one, or had any customers that have, so I can’t comment on it other than to say it’s another option you can look at.
There may be other options out there, and if so, feel free to post them in the comments. These are just the ones I’m familiar with :-)