97

我正在尝试跟踪和记录使用 PHP 访问我的网站的用户/访问者$_SERVER['REMOTE_ADDR']。PHP中IP地址跟踪的典型方法。

但是,我使用 CloudFlare 进行缓存等,并以 CloudFlare 的形式接收它们的 IP 地址:

108.162.212.* - 108.162.239.*

在仍在使用 CloudFlare 的同时检索实际用户/访问者 IP 地址的正确方法是什么?

4

11 回答 11

290

Extra server variables that are available to cloud flare are:

$_SERVER["HTTP_CF_CONNECTING_IP"] real visitor ip address, this is what you want

$_SERVER["HTTP_CF_IPCOUNTRY"] country of visitor

$_SERVER["HTTP_CF_RAY"]

$_SERVER["HTTP_CF_VISITOR"] this can help you know if its http or https

you can use it like this:

if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
  $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
}

If you do this, and the validity of the visiting IP address is important, you might need to verify that the $_SERVER["REMOTE_ADDR"] contains an actual valid cloudflare IP address, because anyone can fake the header if he was able to connect directly to the server IP.

于 2013-02-20T17:03:08.153 回答
18

更新:CloudFlare 发布了一个mod_cloudflare用于 apache 的模块,该模块将记录并显示实际的访问者 IP 地址,而不是 cloudflare 访问的那些!https://www.cloudflare.com/resources-downloads#mod_cloudflare(答案:olimortimer

如果您无权访问 apache 运行时,您可以使用下面的脚本,这将允许您检查连接是否通过 cloudflare 并获取用户 IP。

我正在重写我用于另一个问题“ CloudFlare DNS / 直接 IP 标识符”的答案

Cloudflare 的 ips 是公开存储的,因此您可以在此处查看它们,然后检查 ip 是否来自 cloudflare(这将允许我们从 http 标头获取真实 ip HTTP_CF_CONNECTING_IP)。

如果您使用它来禁用所有非 cf 连接,反之亦然,我建议您有一个 php 脚本文件,该文件在所有其他脚本(如 common.php 或 pagestart.php 等)之前调用。

function ip_in_range($ip, $range) {
    if (strpos($range, '/') == false)
        $range .= '/32';

    // $range is in IP/CIDR format eg 127.0.0.1/24
    list($range, $netmask) = explode('/', $range, 2);
    $range_decimal = ip2long($range);
    $ip_decimal = ip2long($ip);
    $wildcard_decimal = pow(2, (32 - $netmask)) - 1;
    $netmask_decimal = ~ $wildcard_decimal;
    return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
}

function _cloudflare_CheckIP($ip) {
    $cf_ips = array(
        '199.27.128.0/21',
        '173.245.48.0/20',
        '103.21.244.0/22',
        '103.22.200.0/22',
        '103.31.4.0/22',
        '141.101.64.0/18',
        '108.162.192.0/18',
        '190.93.240.0/20',
        '188.114.96.0/20',
        '197.234.240.0/22',
        '198.41.128.0/17',
        '162.158.0.0/15',
        '104.16.0.0/12',
    );
    $is_cf_ip = false;
    foreach ($cf_ips as $cf_ip) {
        if (ip_in_range($ip, $cf_ip)) {
            $is_cf_ip = true;
            break;
        }
    } return $is_cf_ip;
}

function _cloudflare_Requests_Check() {
    $flag = true;

    if(!isset($_SERVER['HTTP_CF_CONNECTING_IP']))   $flag = false;
    if(!isset($_SERVER['HTTP_CF_IPCOUNTRY']))       $flag = false;
    if(!isset($_SERVER['HTTP_CF_RAY']))             $flag = false;
    if(!isset($_SERVER['HTTP_CF_VISITOR']))         $flag = false;
    return $flag;
}

function isCloudflare() {
    $ipCheck        = _cloudflare_CheckIP($_SERVER['REMOTE_ADDR']);
    $requestCheck   = _cloudflare_Requests_Check();
    return ($ipCheck && $requestCheck);
}

// Use when handling ip's
function getRequestIP() {
    $check = isCloudflare();

    if($check) {
        return $_SERVER['HTTP_CF_CONNECTING_IP'];
    } else {
        return $_SERVER['REMOTE_ADDR'];
    }
}

使用脚本非常简单:

$ip = getRequestIP();
$cf = isCloudflare();

if($cf) echo "Connection is through cloudflare: <br>";
else    echo "Connection is not through cloudflare: <br>";

echo "Your actual ip address is: ". $ip;
echo "The server remote address header is: ". $_SERVER['REMOTE_ADDR'];

该脚本将向您显示真实的 IP 地址以及请求是否为 CF!

于 2016-07-11T01:37:54.330 回答
12

自从提出并回答了这个问题,CloudFlare 发布mod_cloudflare了 Apache,它记录并显示实际的访问者 IP 地址,而不是 CloudFlare 地址:

https://www.cloudflare.com/resources-downloads#mod_cloudflare

于 2015-05-06T13:49:06.803 回答
11

Cloudflare 会向您的服务器发送一些额外的请求标头,包括CF-Connecting-IP我们可以$user_ip使用以下简单的单线将其存储到 (如果已定义)中:

$user_ip = (isset($_SERVER["HTTP_CF_CONNECTING_IP"])?$_SERVER["HTTP_CF_CONNECTING_IP"]:$_SERVER['REMOTE_ADDR']);
于 2016-04-16T17:03:05.807 回答
3

很难将 HTTP_CF_CONNECTING_IP 转换为 REMOTE_ADDR。所以你可以使用 apache (.htaccess) 自动前置来做到这一点。这样您就无需考虑是否$_SERVER['REMOTE_ADDR']在所有 PHP 脚本中都具有正确的值。

.ht 访问代码

php_value auto_prepend_file "/path/to/file.php"

php 代码 (file.php)

<?php

define('CLIENT_IP', isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : $_SERVER['REMOTE_ADDR']);

在这里了解更多

于 2018-04-17T13:45:47.733 回答
2

HTTP_CF_CONNECTING_IP 仅在您使用 cloudflare 时才有效,也许您转移您的站点或删除 cloudflare 您会忘记该值,因此请使用此代码。

$ip=$_SERVER["HTTP_CF_CONNECTING_IP"];
if (!isset($ip)) {
  $ip = $_SERVER['REMOTE_ADDR'];
}
于 2017-04-17T13:36:54.947 回答
2

在 Laravel 中将以下行添加到AppServiceProvider

...
use Symfony\Component\HttpFoundation\Request;


...
public function boot()
{
    Request::setTrustedProxies(['REMOTE_ADDR'], Request::HEADER_X_FORWARDED_FOR);
}

现在您可以使用request()->ip().

在这里阅读更多。

于 2020-11-20T12:38:03.407 回答
1

当您使用 CloudFlare 时,您的服务器和用户之间的所有请求都通过 CloudFlare 服务器进行路由。

在这种情况下,有两种方法可以获取用户的真实 IP 地址:

  1. 通过 CloudFlare 服务器添加的额外服务器标头
  2. 在您的服务器上添加 CloudFlare Apache/NGINX 模块。

方法 1:通过额外的服务器标头获取 IP

您可以使用以下代码获取用户的 IP 地址:

$user_ip = (isset($_SERVER["HTTP_CF_CONNECTING_IP"]) $_SERVER["HTTP_CF_CONNECTING_IP"]:$_SERVER['REMOTE_ADDR']);

它是如何工作的?

CloudFlare 在请求中添加了一些额外的服务器变量,如下所示:

$_SERVER["HTTP_CF_CONNECTING_IP"]- 用户的真实 IP 地址

$_SERVER["HTTP_CF_IPCOUNTRY"]- 用户的 ISO2 国家

$_SERVER["HTTP_CF_RAY"]用于登录目的的特殊字符串

在上面的代码中,我们正在检查是否$_SERVER["HTTP_CF_CONNECTING_IP"]已设置。如果它在那里,我们将考虑作为用户的 IP 地址,否则我们将使用默认代码作为$_SERVER['REMOTE_ADDR']

方法 2:在您的服务器上安装 Cloudflare 模块

于 2017-08-12T23:22:06.520 回答
1

Cloudflare 可以选择在域的网络管理页面的标头中使用“伪 IPv4”地址。您可以选择添加或覆盖发送到服务器的标头。

Cloudflare 仪表板 |  网络

从文档中:

什么是伪 IPv4?

作为加速采用 IPv6 的权宜之计,Cloudflare 提供了 Pseudo IPv4,它在需要 IPv4 地址的遗留应用程序中支持 IPv6 地址。目标是使用 E 类 IPv4 地址空间为每个 IPv6 地址提供几乎唯一的 IPv4 地址,该地址空间被指定为实验性的,通常不会看到流量。要了解更多信息,请参见此处

选项

  • 添加标头:仅添加附加Cf-Pseudo-IPv4标头
  • 覆盖标头:用伪 IPv4 地址覆盖现有的标头Cf-Connecting-IP和标头。X-Forwarded-For

注意:我们建议将此设置保留为“关闭”,除非您有特殊需要。

您可以从 Cloudflare 的这篇文章中了解有关此功能的更多信息,其中详细介绍了他们如何尝试在 IPv4 中的 32 位空间中容纳 IPv6 的 128 位地址空间。

如果您想知道 IPv6 地址和伪 IPv4,Cloudflare 会Cf-Connecting-IPv6在启用伪 IPv4 时添加一个标头。这可用于衡量有多少流量来自 IPv6 网络上的设备,如果在投资更新仍受 IPv4 限制的系统之前需要一个可靠的数字来显示管理,这将非常有用。

于 2021-07-16T05:33:21.127 回答
0

对于 magento 1.x 用户(我还没有尝试过 magento 2.0),请查看https://tall-paul.co.uk/2012/03/13/magento-show-remote-ip-in-cloudflare-the- right-way/需要更改 app/etc/local.xml 并添加:HTTP_CF_CONNECTING_IP

于 2017-03-31T03:44:38.457 回答
0

最好在线检查 cloudflare ip 范围(因为它可能随时更改)然后检查它是否来自 cloudflare。

Cloudflare IP txt

如果源是 Cloudflare,您可以使用它$_SERVER['HTTP_CF_CONNECTING_IP']来获取您的客户端请求 IP 地址,但将其用于所有请求并不安全,因为它可以由任何用户在请求标头中发送来欺骗您。

您可以使用以下代码获取客户端请求的真实 IP 地址:

function _getUserRealIP() {
    $ipaddress = '';
    if(isset($_SERVER['REMOTE_ADDR']))
        $ipaddress = $_SERVER['REMOTE_ADDR'];
    else if (isset($_SERVER['HTTP_CLIENT_IP']))
        $ipaddress = $_SERVER['HTTP_CLIENT_IP'];
    else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
        $ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
    else if(isset($_SERVER['HTTP_X_FORWARDED']))
        $ipaddress = $_SERVER['HTTP_X_FORWARDED'];
    else if(isset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
        $ipaddress = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
    else if(isset($_SERVER['HTTP_FORWARDED_FOR']))
        $ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
    else if(isset($_SERVER['HTTP_FORWARDED']))
        $ipaddress = $_SERVER['HTTP_FORWARDED'];
    else
        $ipaddress = 'UNKNOWN';
    return $ipaddress;
}

function _readCloudflareIps()
{
    $file = file("https://www.cloudflare.com/ips-v4",FILE_IGNORE_NEW_LINES);
    return $file;
}

function _checkIpInRange($ip, $range) {
    if (strpos($range, '/') == false)
        $range .= '/32';

    // $range is in IP/CIDR format eg 127.0.0.1/24
    list($range, $netmask) = explode('/', $range, 2);
    $range_decimal = ip2long($range);
    $ip_decimal = ip2long($ip);
    $wildcard_decimal = pow(2, (32 - $netmask)) - 1;
    $netmask_decimal = ~ $wildcard_decimal;
    return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
}

function _checkIsCloudflare($ip) {
    $cf_ips = _readCloudflareIps();
    $is_cf_ip = false;
    foreach ($cf_ips as $cf_ip) {
        if (_checkIpInRange($ip, $cf_ip)) {
            $is_cf_ip = true;
            break;
        }
    }
    return $is_cf_ip;
}

function getRealIp()
{
    $httpIp = _getUserRealIP();
    $check = _checkIsCloudflare($httpIp);
    if ($check) {
        return $_SERVER['HTTP_CF_CONNECTING_IP'];
    }else{
        return $httpIp;
    }
}

将此函数导入您的代码后,您只需调用getRealIp()如下函数:

$userIp = getRealIp();
echo $userIp();
于 2022-02-10T06:14:02.153 回答