Published
Author
Circle56
Reading time

Restore CloudFlare visitor IP in Magento

If your site is behind a proxy such as CloudFlare, getting the "real" visitor IP is an easy addition to the local.xml file and does not need any third party modules installed.

<?xml version="1.0"?>
<config>
    <global>
        .....
        .....
        <remote_addr_headers>
            <header1>HTTP_CF_CONNECTING_IP</header1>
        </remote_addr_headers>
    </global>
</config>

So what's actually going on behind the scenes? In the file /app/code/core/Mage/Core/Model/Session/Abstract/Varien.php

<?php

class Mage_Core_Model_Session_Abstract_Varien extends Varien_Object
{    
    /**
        * Retrieve unique user data for validator
        *
        * @return array
        */
    public function getValidatorData()
    {
        $parts = array(
            self::VALIDATOR_REMOTE_ADDR_KEY             => '',
            self::VALIDATOR_HTTP_VIA_KEY                => '',
            self::VALIDATOR_HTTP_X_FORVARDED_FOR_KEY    => '',
            self::VALIDATOR_HTTP_USER_AGENT_KEY         => ''
        );

        // collect ip data
        if (Mage::helper('core/http')->getRemoteAddr()) {
            $parts[self::VALIDATOR_REMOTE_ADDR_KEY] = Mage::helper('core/http')->getRemoteAddr();
        }
        if (isset($_ENV['HTTP_VIA'])) {
            $parts[self::VALIDATOR_HTTP_VIA_KEY] = (string)$_ENV['HTTP_VIA'];
        }
        if (isset($_ENV['HTTP_X_FORWARDED_FOR'])) {
            $parts[self::VALIDATOR_HTTP_X_FORVARDED_FOR_KEY] = (string)$_ENV['HTTP_X_FORWARDED_FOR'];
        }

        // collect user agent data
        if (isset($_SERVER['HTTP_USER_AGENT'])) {
            $parts[self::VALIDATOR_HTTP_USER_AGENT_KEY] = (string)$_SERVER['HTTP_USER_AGENT'];
        }

        return $parts;
    }
}

The getValidatorData function calls getRemoteAddr in the 'core/http' helper /app/code/core/Mage/Core/Helper/Http.php

<?php

class Mage_Core_Helper_Http extends Mage_Core_Helper_Abstract
{

    /**
        * 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 ? inet_pton($this->_remoteAddr) : $this->_remoteAddr;
    }
}

This function loops through the remote_addr_headers XML node that we added in the local.xml config and returns the first matching request header variable in the $_SERVER superglobal, falling back to the REMOTE_ADDR if no other match was found.

Of course the ideal solution is to reinstate the visitor IP on the web server level, but this is not always possible. For Nginx is fairly easy but Apache you may need to use a propietary CloudFlare mod.