PHP: How to detect / get the real client IP address of website visitors?

by Yang Yang on March 4, 2010

Share This Article:
Subscribe to Kavoir: blog feed

It may seem simple at first because most of us should be relying on the server side environmental variable REMOTE_ADDR solely for client IP addresses:

echo $_SERVER['REMOTE_ADDR'];

Yet it’s barely enough to get the real IP for a variety of circumstances such as when the user is visiting your website from a proxy server. To everyone’s surprise, there are a lot more environmental variables regarding client IP address than just the most straightforward one, REMOTE_ADDR. Consider this snippet in the attempt to detect the real source IP address of the request:

function get_ip_address() {
    foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key) {
        if (array_key_exists($key, $_SERVER) === true) {
            foreach (explode(',', $_SERVER[$key]) as $ip) {
                if (filter_var($ip, FILTER_VALIDATE_IP) !== false) {
                    return $ip;
                }
            }
        }
    }
}

It first searches through a series of possible environmental variables that may contain the client IP address and uses whichever that is set and then extract the potential IP value to be validated. After successful validation by the PHP5 filter_var() function, the value is returned. You better not change the order these variable names are placed in the literal array.

This approach is much more sophisticated than just looking at REMOTE_ADDR but it’s far from mess-proof because it relies on the HTTP header information which can be easily manipulated anywhere along the way the request is routed to your server / website.

Share This Article:
Subscribe to Kavoir: blog feed

You should also read:

{ 11 comments… read them below or add one }

Omoba March 27, 2010 at 1:10 am

I got you stuff, really looks nice and tactical. But I tried to call the function this way:
echo get_ip_address(); but generated this error: Fatal error: Call to undefined function filter_var() in C:\wamp\www\php-bank\create-directory\index.php on line 10, That is the error that it generated, please help! I would love to get the reply in my mail box please! Thanks.

Reply

Yang Yang March 28, 2010 at 8:21 am

You may want to upgrade to the latest PHP version. :) filter_var() is a recent addition to the native functions.

Reply

Alphonse September 15, 2010 at 11:30 pm

$client_addr = filter_var((!empty($_SERVER['HTTP_CLIENT_IP']))? $_SERVER['HTTP_CLIENT_IP']:
(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))? $_SERVER['HTTP_X_FORWARDED_FOR']:
(!empty($_SERVER['REMOTE_ADDR']))? $_SERVER['REMOTE_ADDR']:’UNKNOWN’, FILTER_SANITIZE_STRING);

Reply

Yang Yang September 18, 2010 at 2:25 pm

This is not preferable because it jeopardizes the readability of the code.

Human efficiency comes way higher priority than that of machines or storage.

Reply

Marcel March 7, 2011 at 8:02 pm

Thanks for script, I’ll try, it seems very useful.

Reply

niko March 22, 2011 at 1:50 pm

This is the most elegant code I’ve seen for the job.
Nice work!

Reply

Robin Jakobsson June 9, 2011 at 4:44 pm

Very nice snippet, thanks a lot!

Reply

Steve August 30, 2011 at 11:07 pm

Hello, nice job here! I totally agree with Yang Yang on readability being the most important thing. Actually, for the sake of it, 2 extra things I’d do on your function are:
- use a regex and avoid one extra level of indentation (on the filter_var line)
- declare the array before the foreach (might have some speed benefits also, if an array is otherwise build at every cycle iteration – not sure on this; main point is nevertheless to be able to break long constant line in multiple ones, though, so o improve readability);
But hey, again, cool solution of yours ;-)

Reply

en4ce November 17, 2011 at 10:19 pm

thanks! that works great

Reply

Rutger December 20, 2011 at 5:58 pm

ip-spoofing get’s realy easy with this, just adding an extra header in enough.

Reply

anshu February 7, 2012 at 3:07 pm

[ $client_addr = filter_var((!empty($_SERVER['HTTP_CLIENT_IP']))? $_SERVER['HTTP_CLIENT_IP']:
(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))? $_SERVER['HTTP_X_FORWARDED_FOR']:
(!empty($_SERVER['REMOTE_ADDR']))? $_SERVER['REMOTE_ADDR']:’UNKNOWN’, FILTER_SANITIZE_STRING) ]

Reply

Leave a Comment

{ 1 trackback }

Previous post:

Next post: