Die Jagd nach der IP: Warum $_SERVER['REMOTE_ADDR'] allein nicht mehr reicht
\n\nStellen Sie sich vor: Sie haben ein tolles Bildungsprojekt mit PHP gestartet. Alles läuft rund, die Nutzer sind zufrieden. Doch dann wollen Sie Besucher loggen, geografisch zuordnen oder etwa vor Stimmenmanipulation schützen. Und plötzlich – Peng! – stellen Sie fest, dass die Hälfte Ihrer „einzigartigen“ Besucher dieselbe IP hat: 127.0.0.1 oder 10.0.0.1. Kommt Ihnen das bekannt vor?
Das Problem, die echte Client-IP in PHP zu ermitteln, ist ein Klassiker, der nichts an Aktualität verloren hat. Die Frage auf Stack Overflow erhält tausende Aufrufe, und Anfänger (und nicht nur sie) tappen immer wieder in dieselben Fallen. Im Jahr 2025, in dem Cloudflare, AWS, Nginx und unzählige CDN-Proxys zum Standard gehören, ist es wie Kaffeesatzlesen, sich nur auf $_SERVER['REMOTE_ADDR'] zu verlassen.
In diesem Artikel geben wir Ihnen nicht nur eine fertige Funktion. Wir analysieren die Anatomie der HTTP-Header, lernen, Freund von Feind zu unterscheiden, und schreiben eine sichere und zuverlässige Methode zur Ermittlung der Besucher-IP in PHP, die Sie 2025 nicht im Stich lässt. Los geht's!
\n\nREMOTE_ADDR: Ihr treuer, aber blinder Hund
\n\nFangen wir mit den Grundlagen an. $_SERVER['REMOTE_ADDR'] ist die IP-Adresse desjenigen, der sich direkt mit Ihrem Webserver verbindet. Wenn Ihre Website auf einem dedizierten Server ohne Proxy hängt, ist das die Client-IP. Einfach und zuverlässig.
Doch in der modernen Welt ist das selten der Fall. Meistens liegt zwischen dem Benutzer und Ihrem PHP-Skript eine ganze Kette:
\n- \n
- CDN (Cloudflare, Fastly) \n
- Load Balancer (Nginx, HAProxy) \n
- Reverse Proxy (Varnish, Nginx) \n
- Hosting mit Shared-Architektur \n
Und hier zeigt REMOTE_ADDR die IP des letzten Glieds (z. B. Ihres Load Balancers), nicht die des echten Benutzers. Für eine Bildungsseite ist das eine Katastrophe: Sie können einen Studenten aus Berlin nicht von einem aus New York unterscheiden, und Ihr Anti-Spam-System blockiert alle pauschal.
HTTP-Header: Wo versteckt sich die echte IP?
\n\nUm die echte Client-IP zu übermitteln, fügen Proxy-Server spezielle HTTP-Header hinzu. Die beliebtesten im Jahr 2025 sind:
\n\n1. X-Forwarded-For (XFF)
\nDer klassische Header. Kann eine Kette von IPs enthalten: client_ip, proxy1_ip, proxy2_ip. Die erste IP in der Liste ist der echte Client. Aber es gibt einen Haken: Dieser Header kann von jedem Angreifer gefälscht werden, wenn Ihre Infrastruktur ihn nicht bereinigt.
2. X-Real-IP
\nDer Lieblings-Header von Nginx. Enthält nur eine IP – die echte. Einfacher und sicherer, wird aber nicht von allen Proxys unterstützt.
\n\n3. CF-Connecting-IP (Cloudflare)
\nEin spezifischer Header von Cloudflare. Wenn Sie deren CDN nutzen, ist dies die zuverlässigste Option. Aber nur, wenn Sie Cloudflare vertrauen (und das sollten Sie, sie machen einen guten Job).
\n\n\n Wichtig: Vertrauen Sie niemals den Headern\n\nX-Forwarded-ForundX-Real-IPvon unbekannten Quellen. Ein Angreifer kann jede beliebige IP einfügen, und Ihr System würde sie als echt betrachten.\n
Beste Praxis 2025: Mehrschichtiger Schutz
\n\nWas also tun? Wir können nicht einfach die erste IP aus X-Forwarded-For nehmen – das ist gefährlich. Aber wir können die Proxys auch nicht ignorieren. Die Lösung ist eine Whitelist vertrauenswürdiger Proxys.
Die Idee ist einfach: Wir kennen die IP-Adressen unserer Load Balancer und CDNs. Wenn die Anfrage von ihnen kommt, vertrauen wir den Headern. Wenn nicht, verwenden wir REMOTE_ADDR.
Hier ist eine funktionierende PHP-Klasse, die diese Logik umsetzt:
\n\n<?php\n\nclass ClientIP\n{\n /**\n * Liste vertrauenswürdiger IP-Adressen und Subnetze (CIDR).\n * Fügen Sie hier die IPs Ihrer Proxys, CDNs und Load Balancer hinzu.\n */\n private static array $trustedProxies = [\n '127.0.0.1', // localhost\n '10.0.0.0/8', // private Netzwerke\n '172.16.0.0/12',