Does anyone know why this happens?
This happens because CloudFlare acts as a proxy, the user connects to a nearest CloudFlare server, and then CloudFlare connects to your server, if necessary, as some content may be cached. If it is not cached CloudFlare will get a response from the server and will deliver it to the user.
Basically:
User (1.2.3.4) - > CloudFlare (5.6.7.8) - > Server (9.1.2.3)
Assuming this to be IPs, you will get 5.6.7.8 by the common method.
Is there a better way to get the user's IP?
Just because it serves as a proxy it includes the header X-Forwarded-For
, but this is not as secure, but it also includes CF-Connecting-IP
.
The header X-Forwarded-For
:
If this heading does not exist, CloudFlare will create:
X-Forwarded-For: 1.2.3.4
So, 1.2.3.4 is the client IP.
If there is already a defined one, CloudFlare will add:
X-Forwarded-For: 1.2.3.4, A.A.A.A, X.X.X.X
In theory, the first would be the client IP, since X-Forwarded-For
is defined by proxy, but in practice the situation is different because the user can falsify X-Forwarded-For
, so the latter would be the true one.
The header CF-Connecting-IP
:
CloudFlare creates this header, this will provide the last IP, this is usually the same as X-Forwarded-For
, in a "normal" situation.
Quick solution
PHP:
$_SERVER["REMOTE_ADDR"] = $_SERVER["HTTP_CF_CONNECTING_IP"];
This will get the informed header containing the user's IP and will replace in the $_SERVER["REMOTE_ADDR"]
variable, this can reduce the necessary changes. ;)
However, this creates a problem because if the traffic does not pass through CloudFlare the user can falsify the IP by sending such a header.
Solution
PHP:
$_SERVER["REMOTE_ADDR"] = $_SERVER["HTTP_CF_CONNECTING_IP"];
NGINX:
server {
listen 80;
# ...
# Impede acesso fora do CloudFlare:
allow 103.21.244.0/22;
allow 103.22.200.0/22;
allow 103.31.4.0/22;
allow 104.16.0.0/12;
allow 108.162.192.0/18;
allow 131.0.72.0/22;
allow 141.101.64.0/18;
allow 162.158.0.0/15;
allow 172.64.0.0/13;
allow 173.245.48.0/20;
allow 188.114.96.0/20;
allow 190.93.240.0/20;
allow 197.234.240.0/22;
allow 198.41.128.0/17;
allow 199.27.128.0/21;
allow 2400:cb00::/32;
allow 2405:8100::/32;
allow 2405:b500::/32;
allow 2606:4700::/32;
allow 2803:f800::/32;
allow 2c0f:f248::/32;
allow 2a06:98c0::/29;
deny all;
#####################################
# Define para salvar todos os logs com o IP do cliente:
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 199.27.128.0/21;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;
real_ip_header CF-Connecting-IP;
#####################################
# ...
}
If you are using Apache (or any other) look for equivalent functions.
This will ensure that you can only access the website if it passes through CloudFlare, which will contain HTTP_CF_CONNECTING_IP
, in addition NGINX will save all the logs using client IP and not more of the proxy. ;)