Magento Show Remote IP When using cloudFlare

There’s an update to this article here. The below article is here as tutorial for writing a helper in Magento, but if you just want to get the visitor IP behind cloudflare you’re better using the ‘proper’ method described here (click the ‘here’, it’s a link despite my stupid CSS not making it obvious!)

This week we setup one of our eCommerce sites to use CloudFlare, the CDN and optimisation solution for websites.  This enabled us to deal with some sporadic burts of traffic we were seeing by taking the majority of the load off the site.  One problem we encountered though was how to get a visitor’s IP address (which is shown in Magento agaianst their order).  The standard PHP method of getting the IP from the server header(which magento uses) doesn’t work, because the IP shown here is the Cloudflare server address.

Helpfully, Cloudflare do provide us with the visitor IP, in a custom header (HTTP_CF_CONNECTING_IP) they pass in as seen here

So, how to get Magento to use this value instead of the SERVER header?  Well, thanks to Magento’s override system it’s actually quite simple.  First off, we need to figure out where Magento actually gets its IP from.  After some searching I found out it’s done in one of the core helpers, specifically the ‘Http’ one:

/app/core/Mage/Core/Helper/Http.php

This has some other stuff in it, but the only bit we’re interested in is this method:

 /**
     * Retrieve Client Remote Address
     *
     * @param bool $ipToLong converting IP to long format
     * @return string IPv4|long
     */
    public function getRemoteAddr($ipToLong = false)
    {
        if (is_null($this->_remoteAddr)) {
            $headers = $this->getRemoteAddrHeaders();
            foreach ($headers as $var) {
                if ($this->_getRequest()->getServer($var, false)) {
                    $this->_remoteAddr = $_SERVER[$var];
                    break;
                }
            }

            if (!$this->_remoteAddr) {
                $this->_remoteAddr = $this->_getRequest()->getServer('REMOTE_ADDR');
            }
        }

        if (!$this->_remoteAddr) {
            return false;
        }

        return $ipToLong ? ip2long($this->_remoteAddr) : $this->_remoteAddr;
    }

Luckily, Magento (and the underlying PHP) allow us some pretty nifty functionality that let’s us override just this method, while leaving the rest of the helper untouched.  So, lets get down to it.

First off, we need to create our file structure.  For this helper the file structure we need looks like this:

 

 

 

 

 

 

 

As you can see we’re ceating our helper in the ‘local’ codepool, under a namespace of ‘TallPaul’ and I’m creating a new ‘http’ module.  The 2 files we’re concerned with here are the ‘Config.xml’ which initialises our module and defines what it does, and ‘cloudflarehttp.php’ which contains the code for our helper.

here’s /app/code/local/Tallpaul/http/etc/config.xml:

<?xml version="1.0"?>
    <config>
        <modules>
            <Tallpaul_http>
                <version>0.1.0</version>
            </Tallpaul_http>
        </modules>
        <global>
           <helpers>
              <core>
                  <rewrite>
                      <http>Tallpaul_Http_Helper_Cloudflarehttp</http>
                 </rewrite>
              </core>
           </helpers>
        </global>
  </config>

here we define our module, and tell it we want to ‘rewrite’ the ‘http’ module which is a global helper which lives in ‘core’.  That’s pretty straightforward, and really shows some of the power of Magento, that’s all it takes to override one of the core modules.  Now, because we don’t want to override the whole module, we can just extend from it as a base class like this:

/app/code/local/TallPaul/http/helper/cloudflarehttp.php

<?php
class Tallpaul_Http_Helper_Cloudflarehttp extends Mage_Core_Helper_Http{

    public function getRemoteAddr($ipToLong = false){        
        if (is_null($this->_remoteAddr)) {
            if ( $_SERVER["HTTP_CF_CONNECTING_IP"] ) {
                $this->_remoteAddr = $_SERVER["HTTP_CF_CONNECTING_IP"];
            }
            else    {
                $this->_remoteAddr = parent::getRemoteAddr(false);
            }
        }    
        return $ipToLong ? ip2long($this->_remoteAddr) : $this->_remoteAddr;
}
}
?>

because we’re extending the base class (ie: the core http helper) our class has all the functionality of the base without us hing to code it all.  We can also even access the base class’ implementation of the function we’re overriding (using ‘parent::’) to access that functionality.  In our class we try and get the Visitor IP from the cloudflare header, and if we don’t get anything back just pass the request through to the base implementation.

All that’s left to do now is activate our module in Magento.  We do this by adding an xml file into /app/etc/modules like this:

/app/etc/modules/TallPaul.xml

<?xml version="1.0"?>
       <config>
             <modules>
                  <Tallpaul_Http>
                       <active>true</active>
                       <codePool>local</codePool>
                  </Tallpaul_Http>
            </modules>
     </config>

And that’s all you need.  Clear your magento cache and you should be good to go.

 

You can download the code for this article here




10 Comments

Wow, great! Thanks for this. I’m now on day 1 with Cloudflare, and hoping things work out.

I was wondering how hard it would be to extend this to the “Online Customers” view in the admin panel? I’m extremely far from being a php programmer, but am willing to take a shot at it. I’ll post back here when/if successful. Thanks again.

I would have thought that, as long as the online customers section uses the standard Magento helper for HTTP then it should just work anyway… all my component does is override that standard component, so anything relying on that should automatically use the new class.

Let me know how you get on though, I’d be interested to know if anything else is required… maybe some more magento configuration to get it working.

Looks like we both made this too hard. Looking at app/etc/local.xml.additional describes the changes to make to the local.xml for just this situation. Basically, edit:

/app/etc/local.xml

In the section (like after the session storage setting), add:

HTTP_CF_CONNECTING_IP
REMOTE_ADDR

Now Online Customers is working, and orders show the correct IP address! Thanks for your help in getting me in the right direction.

WordPress is screwing up my tags. Let me try wrapping in quotes:

“in the section (like after the session storage setting), add:

HTTP_CF_CONNECTING_IP
REMOTE_ADDR

WP is still stripping my codes. The file is pretty easy to follow though.

Admin, you can contact me through email with any questions. Feel free to edit/delete my replies to clean up your site or get the code to show up.

Leave a Reply

Your email address will not be published. Required fields are marked *