Caça ao IP: por que só $_SERVER['REMOTE_ADDR'] já não basta?
\n\nImagine: você lançou um projeto educacional incrível em PHP. Tudo voa, os usuários estão felizes. Mas aí decide adicionar logging de visitantes, geolocalização ou, digamos, proteção contra votos fraudulentos. E então — puf! — descobre que metade dos seus visitantes "únicos" tem o mesmo IP: 127.0.0.1 ou 10.0.0.1. Familiar?
O problema de obter o IP real do cliente em PHP é um clássico que nunca perde a relevância. A pergunta no Stack Overflow acumula milhares de visualizações, e iniciantes (e não só) continuam pisando nos mesmos ancinhos. Em 2025, com Cloudflare, AWS, Nginx e uma infinidade de proxies CDN como padrão de facto, confiar apenas em $_SERVER['REMOTE_ADDR'] é como ler borra de café.
Neste artigo, não vamos apenas dar uma função pronta. Vamos dissecar a anatomia dos headers HTTP, aprender a distinguir amigos de inimigos e escrever uma forma segura e confiável de obter o IP do visitante em PHP que não vai te decepcionar em 2025. Vamos nessa!
\n\nREMOTE_ADDR: seu cão fiel, mas cego
\n\nComeçamos pela base. $_SERVER['REMOTE_ADDR'] é o IP de quem se conectou diretamente ao seu servidor web. Se o seu site está em um servidor dedicado sem proxy, esse é o IP do cliente. Simples e confiável.
Mas no mundo moderno, isso é raro. Na maioria das vezes, entre o usuário e seu script PHP existe uma cadeia inteira:
\n- \n
- CDN (Cloudflare, Fastly) \n
- Balanceador de carga (Nginx, HAProxy) \n
- Proxy reverso (Varnish, Nginx) \n
- Hospedagem com arquitetura compartilhada \n
E aí o REMOTE_ADDR mostra o IP do último elo (por exemplo, do balanceador), não o do usuário real. Para um site educacional, é um desastre: você não conseguirá distinguir um aluno de São Paulo de um aluno de Lisboa, e o sistema antispam vai bloquear todo mundo.
Headers HTTP: onde o IP real se esconde?
\n\nPara transmitir o IP real do cliente, os servidores proxy adicionam headers HTTP especiais. Os mais populares em 2025 são:
\n\n1. X-Forwarded-For (XFF)
\nO header clássico. Pode conter uma cadeia de IPs: client_ip, proxy1_ip, proxy2_ip. O primeiro IP da lista é o cliente real. Mas tem um porém: qualquer invasor pode falsificar esse header se sua infraestrutura não o limpar.
2. X-Real-IP
\nO header favorito do Nginx. Contém apenas um IP — o real. Mais simples e seguro, mas não é suportado por todos os proxies.
\n\n3. CF-Connecting-IP (Cloudflare)
\nHeader específico do Cloudflare. Se você usa a CDN deles, é a opção mais confiável. Mas só se você confia no Cloudflare (e deve, eles são bons nisso).
\n\n\n Importante: Nunca confie nos headers\n\nX-Forwarded-ForeX-Real-IPde fontes desconhecidas. Um invasor pode inserir qualquer IP, e seu sistema vai tratá-lo como real.\n
Melhor prática 2025: proteção em camadas
\n\nEntão, o que fazer? Não podemos simplesmente pegar o primeiro IP do X-Forwarded-For — é perigoso. Mas também não podemos ignorar os proxies. A solução é uma lista branca de proxies confiáveis.
A ideia é simples: sabemos os IPs dos nossos balanceadores e CDN. Se a requisição veio deles, confiamos nos headers. Caso contrário, usamos o REMOTE_ADDR.
Aqui está uma classe PHP funcional que implementa essa lógica:
\n\n<?php\n\nclass ClientIP\n{\n /**\n * Lista de IPs e sub-redes confiáveis (CIDR).\n * Adicione aqui os IPs dos seus proxies, CDN, balanceadores.\n */\n private static array $trustedProxies = [\n '127.0.0.1', // localhost\n '10.0.0.0/8', // redes privadas\n '172.16.0.0/12',