Getting the client IP address: REMOTE_ADDR, HTTP_X_FORWARDED_FOR, what else could be useful?



I understand it's a standard practice to look at both these variables. Of course they can easily be spoofed. I'm curious how often can you expect these values (especially the HTTP_X_FORWARDED_FOR) to contain genuine information and not just be scrambled or have their values stripped away?

Anyone with the experience or statistics on this stuff?

Is there anything else that can be useful for the task of getting the client's IP address?


Posted 2009-02-09T10:12:29.697

Reputation: 20 444

Note the question and answers both use HTTP_ prefix which is a particular implementation detail of ASP.NET v1.0-v4.x, when HTTP request headers are added to ServerVariables collection. Another example is REMOTE_ADDR, which has it's own API in ASP.NET Core.

– yzorg – 2017-12-11T14:24:10.487



It depends on the nature of your site.

I happen to work on a bit of software where IP tracking is important, and within a field consumed by parter sites I'd guess some 20% - 40% of requests are either detectably spoofed IPs or headers blanked out, depending on the time of day and where they came from. For a site which gets organic traffic (i.e. not through partners) I'd expect a much higher ratio of good IPs.

As Kosi said, be careful what you're doing with this - IPs are in no way a reliable way to identify unique visitors.


Posted 2009-02-09T10:12:29.697

Reputation: 62 237


In addition to REMOTE_ADDR and HTTP_X_FORWARDED_FOR there are some other headers that can be set such as:

  • HTTP_X_FORWARDED_FOR can be comma delimited list of IPs

I found the code on the following site useful:


Posted 2009-02-09T10:12:29.697

Reputation: 7 051

Is this list somehow complete, meaning is it covering over 90% of all proxies? – basZero – 2014-01-10T17:13:41.143


I don't think those headers should have the HTTP_ prefix...a little searching turned up

– lmsurprenant – 2014-04-01T18:34:58.250

There is also a Referer now, as per RFC 7239

– Kevin Doyon – 2015-12-04T21:50:25.493


I've ported Grant Burton's PHP code to an ASP.Net static method callable against the HttpRequestBase. It will optionally skip through any private IP ranges.

public static class ClientIP
    // based on
    public static string ClientIPFromRequest(this HttpRequestBase request, bool skipPrivate)
        foreach (var item in s_HeaderItems)
            var ipString = request.Headers[item.Key];

        if (String.IsNullOrEmpty(ipString))

        if (item.Split)
            foreach (var ip in ipString.Split(','))
                if (ValidIP(ip, skipPrivate))
                    return ip;
            if (ValidIP(ipString, skipPrivate))
                return ipString;

    return request.UserHostAddress;

private static bool ValidIP(string ip, bool skipPrivate)
    IPAddress ipAddr;

    ip = ip == null ? String.Empty : ip.Trim();

    if (0 == ip.Length
        || false == IPAddress.TryParse(ip, out ipAddr)
        || (ipAddr.AddressFamily != AddressFamily.InterNetwork
            && ipAddr.AddressFamily != AddressFamily.InterNetworkV6))
        return false;

    if (skipPrivate && ipAddr.AddressFamily == AddressFamily.InterNetwork)
        var addr = IpRange.AddrToUInt64(ipAddr);
        foreach (var range in s_PrivateRanges)
            if (range.Encompasses(addr))
                return false;

    return true;

/// <summary>
/// Provides a simple class that understands how to parse and
/// compare IP addresses (IPV4) ranges.
/// </summary>
private sealed class IpRange
    private readonly UInt64 _start;
    private readonly UInt64 _end;

    public IpRange(string startStr, string endStr)
        _start = ParseToUInt64(startStr);
        _end = ParseToUInt64(endStr);

    public static UInt64 AddrToUInt64(IPAddress ip)
        var ipBytes = ip.GetAddressBytes();
        UInt64 value = 0;

        foreach (var abyte in ipBytes)
            value <<= 8;    // shift
            value += abyte;

        return value;

    public static UInt64 ParseToUInt64(string ipStr)
        var ip = IPAddress.Parse(ipStr);
        return AddrToUInt64(ip);

    public bool Encompasses(UInt64 addrValue)
        return _start <= addrValue && addrValue <= _end;

    public bool Encompasses(IPAddress addr)
        var value = AddrToUInt64(addr);
        return Encompasses(value);

private static readonly IpRange[] s_PrivateRanges =
    new IpRange[] { 
            new IpRange("",""),
            new IpRange("",""),
            new IpRange("",""),
            new IpRange("",""),
            new IpRange("",""),
            new IpRange("",""),
            new IpRange("",""),
            new IpRange("","")

/// <summary>
/// Describes a header item (key) and if it is expected to be 
/// a comma-delimited string
/// </summary>
private sealed class HeaderItem
    public readonly string Key;
    public readonly bool Split;

    public HeaderItem(string key, bool split)
        Key = key;
        Split = split;

// order is in trust/use order top to bottom
private static readonly HeaderItem[] s_HeaderItems =
    new HeaderItem[] { 
            new HeaderItem("HTTP_CLIENT_IP",false),
            new HeaderItem("HTTP_X_FORWARDED_FOR",true),
            new HeaderItem("HTTP_X_FORWARDED",false),
            new HeaderItem("HTTP_X_CLUSTER_CLIENT_IP",false),
            new HeaderItem("HTTP_FORWARDED_FOR",false),
            new HeaderItem("HTTP_FORWARDED",false),
            new HeaderItem("HTTP_VIA",false),
            new HeaderItem("REMOTE_ADDR",false)


Posted 2009-02-09T10:12:29.697

Reputation: 1 640

2Thanks for the code. There are a couple of problems with it, though. First, there is an extra return false; in ValidIP. Second, the IpRange class doesn't really handle IPV6 since IPV6 addresses are 128 bits. Maybe System.Numerics.BigInteger from .NET 4 could be used, but it's also possible private ranges are less interesting with IPV6 anyway (?). – Eric Smith – 2012-09-07T14:51:35.933

1Oh, and one other problem: Instead of checking headers from request.Headers, I think you want request.ServerVariables. The latter has keys like HTTP_X_FORWARDED_FOR whereas the former would just be X-Forwarded-For. – Eric Smith – 2012-09-12T23:23:43.137

Would be great if this also supported RFC 7239 :D

– Kevin Doyon – 2015-12-04T21:40:25.693

Headers vs ServerVariables ? – Kiquenet – 2016-11-15T11:48:38.597

@Kevin do you implement code for supports RFC 7239 ? – Kiquenet – 2016-11-15T11:49:20.097


No real answer to your question but:
Generally relying on the clients IP address is in my opinion not a good practice as it is not usable to identify clients in a unique fashion.

Problems on the road are that there are quite a lot scenarios where the IP does not really align to a client:

  • Proxy/Webfilter (mangle almost everything)
  • Anonymizer network (no chance here either)
  • NAT (an internal IP is not very useful for you)
  • ...

I cannot offer any statistics on how many IP addresses are on average reliable but what I can tell you that it is almost impossible to tell if a given IP address is the real clients address.


Posted 2009-02-09T10:12:29.697

Reputation: 15 766

Which is best practices to identify clients in a unique fashion ? Checklist: Not use clients IP address – Kiquenet – 2016-11-15T11:51:09.340


IP + "User Agent" could be a better for unique visitor.


Posted 2009-02-09T10:12:29.697

Reputation: 734


widely spoofed, but generally they don't change from request to request -

– wprl – 2010-02-22T22:15:17.153

nah, user agents aren't very diverse and widely spoofed anyway – annakata – 2009-02-10T20:20:51.777


If you're behind a proxy, you should use X-Forwarded-For:

It is an IETF draft standard with wide support:

The X-Forwarded-For field is supported by most proxy servers, including Squid, Apache mod_proxy, Pound, HAProxy, Varnish cache, IronPort Web Security Appliance, AVANU WebMux, ArrayNetworks, Radware's AppDirector and Alteon ADC, ADC-VX, and ADC-VA, F5 Big-IP, Blue Coat ProxySG, Cisco Cache Engine, McAfee Web Gateway, Phion Airlock, Finjan's Vital Security, NetApp NetCache, jetNEXUS, Crescendo Networks' Maestro, Web Adjuster and Websense Web Security Gateway.

If not, here are a couple other common headers I've seen:


Posted 2009-02-09T10:12:29.697

Reputation: 616