Actual Client IP Address on Azure WebSites with CloudFlare

Microsoft

Over at Vinyl Deals, I use CloudFlare to handle some caching and firewall issues. Like any good server admin, I check my logs to make sure the app is running smoothly so people can find those sweet record deals. But sometimes I notice a strange uptick in traffic on the back-end that doesn't show up in the front-end analytics.

Usually, this means it's just a bot. Often it's Googlebot, which I'm totally fine with. CloudFlare knows about Googlebot (and others) and reports how many pages they crawled. Last night, however, I noticed a huge uptick in traffic on the back-end that wasn't a known bot.

Since Vinyl Deals is an ASP.NET application and I'm hosting on Azure, Application Insights is a natural choice for performance telemetry. Here's what one of my performance timing events looks like in the controller:

TelemetryClient telemetry = new TelemetryClient();
Stopwatch stopwatch;

stopwatch = Stopwatch.StartNew();
// DATABASE OPERATION TO TIME
stopwatch.Stop();

var perfMetric = new Dictionary
     {{"processingTime", stopwatch.Elapsed.TotalMilliseconds}};

var telemetryProperties = new Dictionary
{
    {"AlbumId", id.ToString()},                
};

telemetry.TrackEvent("Retrieve Album Details", telemetryProperties, perfMetric);

This is great, but because of the way CloudFlare works, the IP address logged for this telemetry value might be an internal CloudFlare IP address.

I can use CloudFlare to restrict traffic to bad actors, but restricting the CloudFlare IP would blow the whole thing up. What I need is the actual client IP address.

Thankfully, CloudFlare knows you might want this and it actually does provide it with each call, it's just hidden a bit. The actual client IP address is often found in a custom header called CF-Connecting-IP

Since I'm already using Application Insights, I decided to just include the actual IP as one of the properties on the telemetry call. Since it's just an HTTP header, you can retrieve it with Request.Headers["CF-Connecting-IP"].

Putting the two pieces together, setting the telemetry properties to include this IP address looks like this:

var telemetryProperties = new Dictionary
    {
        {"AlbumId", id.ToString()},
        {"Actual IP", Request.Headers["CF-Connecting-IP"] }
    };

Over in the Application Insights console, it ends up looking like this:

Application Insights console showing custom data that has the Actual Client IP address in it

Check out the CloudFlare post because they have more information in the case that the call is already from a proxy.

Music for this post: Big Grams - Fell in the Sun