Warum im Protokoll oft nicht die Adresse des Besuchers steht
Ein Webserver kennt genau eine Adresse mit Gewissheit: die der Gegenstelle, mit der er die Verbindung aufgebaut hat. Steht davor ein Reverse Proxy, ist das dessen Adresse. MDN beschreibt die Lage nüchtern: Läuft eine Verbindung über Vorwärts- oder Rückwärts-Proxys, sieht der Server nur die Adresse des letzten Proxys, und die ist häufig von geringem Nutzen (MDN). Besonders deutlich wird das, wenn der letzte Sprung ein Load Balancer aus derselben Installation ist – dann trägt jeder Protokolleintrag dieselbe interne Adresse, Tag für Tag, und die Auswertung sieht trotzdem gefüllt aus.
Genau dafür wurde X-Forwarded-For eingeführt. Der Header trägt eine kommagetrennte Liste, in der jeder Proxy die Adresse seiner Gegenstelle anhängt. Am rechten Rand steht damit die Adresse des jüngsten Proxys, am linken Rand die des ursprünglichen Clients – allerdings nur unter einer Bedingung, die MDN ausdrücklich mitschreibt: Die Ordnung gilt unter der Annahme wohlerzogener Clients und Proxys (MDN). Ein Besucher, der den Header selbst mitschickt, ist nicht wohlerzogen. Er muss dafür nichts weiter können, als eine Kopfzeile zu setzen; jede Kommandozeile mit curl reicht dafür aus.
- Zugriffsprotokolle: Jede Zeile trägt die Adresse des letzten Sprungs. Auswertungen über Herkunft, Häufigkeit und Muster laufen damit über den eigenen Proxy statt über den Besucher.
- Sperren und Ratenbegrenzung: Eine Regel, die auf der Adresse des eigenen Load Balancers arbeitet, trifft entweder alle oder keinen. Eine Regel, die auf einem frei gesetzten Wert arbeitet, trifft einen Unbeteiligten.
- Herkunft und Netzklasse: Land, Region und Netzbetreiber werden aus der Adresse abgeleitet – wie das im Detail funktioniert, zeigt die Geo-API. Ist die Adresse falsch, ist die Herkunft falsch, ohne dass ein Fehler sichtbar würde.
- Bot-Erkennung: Verfahren, die Anfragen je Adresse zählen, bewerten bei falscher Zuordnung entweder eine einzige Sammeladresse oder beliebig viele erfundene.
Wie die Kette entsteht und wer welchen Wert anhängt
Der Ablauf lässt sich mit drei Adressen aus dem Dokumentationsbereich durchspielen. Ein Besucher schickt eine Anfrage und setzt selbst X-Forwarded-For: 203.0.113.7. Die Anfrage erreicht zunächst einen fremden Proxy, etwa ein Firmennetz oder einen Anonymisierungsdienst. Dieser Proxy hängt die Adresse an, mit der der Besucher bei ihm angekommen ist, hier 198.51.100.4. Der Header lautet jetzt 203.0.113.7, 198.51.100.4. Danach erreicht die Anfrage den eigenen Load Balancer. Auch er hängt an, was er sieht: die Adresse des fremden Proxys, hier 192.0.2.55. In der Anwendung kommt 203.0.113.7, 198.51.100.4, 192.0.2.55 an, und die Gegenstelle der Verbindung ist der Load Balancer.
Drei Einträge, drei sehr verschiedene Qualitäten. Der erste stammt vom Besucher und ist frei erfunden. Der zweite stammt vom fremden Proxy; inhaltlich ist er vermutlich richtig, aber er kommt aus einer Quelle, die niemand im eigenen Haus kontrolliert. Der dritte stammt vom eigenen Load Balancer und ist der einzige Eintrag mit Beleg: Er wurde von einer Stelle geschrieben, die zum eigenen Aufbau gehört. MDN zieht daraus einen unbequemen Schluss: Die erste vertrauenswürdige Adresse aus X-Forwarded-For gehört möglicherweise einem nicht vertrauenswürdigen Zwischenproxy statt dem eigentlichen Client, ist aber die einzige Adresse, die sich zur Identifikation für Sicherheitszwecke eignet (MDN). Wer den Besucher genau treffen will, bekommt hier keine Zusage – wer eine belastbare Entscheidungsgrundlage will, schon.
Die Frage „Wer war das?“ und die Frage „Wen darf ich sperren?“ führen zu unterschiedlichen Einträgen derselben Liste. Für eine Reichweitenauswertung ohne Folgen taugt der linke Rand, für alles mit Konsequenz nur der eigene Teil der Kette. MDN zieht die Grenze so: Linke, nicht vertrauenswürdige Werte dürfen nur dort verwendet werden, wo die Nutzung gefälschter Werte keinen negativen Effekt hat (MDN). Wie stark sich veränderte Adressen auf die Auswertung auswirken, zeigt der Beitrag zu Chrome IP Protection und Geodaten.
Die Vertrauensgrenze festlegen
Die Vertrauensgrenze ist keine Einstellung, sondern eine Aussage über den eigenen Aufbau: Welche Stellen zwischen Internet und Anwendung gehören mir, und schreiben sie den Header verlässlich? MDN nennt zwei gängige Verfahren, um den ersten belastbaren Eintrag zu finden. Beim ersten wird die Anzahl der Reverse Proxys zwischen Internet und Server konfiguriert; die Liste wird von rechts um diese Anzahl minus eins durchlaufen (MDN). Beim zweiten werden die Adressen oder Adressbereiche der vertrauenswürdigen Reverse Proxys hinterlegt; die Liste wird von rechts durchsucht, alle hinterlegten Adressen werden übersprungen, und die erste nicht passende Adresse ist die gesuchte (MDN). Das zweite Verfahren überlebt einen Umbau der Kette besser, weil es keine Position zählt, sondern Zugehörigkeit prüft.
| Verfahren | Welche Adresse es liefert | Wofür es taugt | Wo es bricht |
|---|---|---|---|
| Adresse der Gegenstelle | der letzte Sprung | Aufbau ganz ohne Proxy | hinter Load Balancer oder Auslieferungsnetz ein einziger, gleichbleibender Wert |
| Linkester Eintrag | was der erste Knoten gemeldet hat | grobe Reichweitenauswertung ohne Folgen | vom Besucher frei setzbar |
| Rechtester Eintrag | was der letzte Proxy angehängt hat | genau eine eigene Proxy-Schicht | bei zwei eigenen Schichten liefert er eine interne Adresse |
| Anzahl eigener Proxys | Position von rechts, Anzahl minus eins | feste, bekannte Kette | eine zusätzliche Schicht verschiebt die Position stillschweigend |
| Liste eigener Proxys | erste Adresse von rechts, die nicht hinterlegt ist | wechselnde Zahl von Schichten | veraltete Adressbereiche nach einem Umzug |
| Eigenes Feld des Auslieferungsnetzes | der Wert, den der Anbieter selbst setzt | wenn dieser Anbieter das Feld verlässlich überschreibt | ohne diesen Anbieter davor ist das Feld frei setzbar |
Eine Voraussetzung steht über allen Verfahren und wird erfahrungsgemäß übersehen: Der Server darf aus dem Internet nicht unmittelbar erreichbar sein. MDN ist hier deutlich – kann der Server direkt aus dem Internet verbunden werden, selbst wenn er zusätzlich hinter einem vertrauenswürdigen Reverse Proxy steht, kann kein Teil der X-Forwarded-For-Liste als vertrauenswürdig oder als geeignet für sicherheitsrelevante Zwecke gelten (MDN). Ein offener Port am Ursprungsserver hebt damit die gesamte Auswertung auf, ganz unabhängig davon, wie sorgfältig sie programmiert ist. Wie man den Ursprung hinter einem Auslieferungsnetz überhaupt im Blick behält, steht im Beitrag zum Ursprung hinter dem Auslieferungsnetz.
Was schiefgeht, wenn die Grenze fehlt
Die Folgen sind keine Randfälle, sie stehen ausformuliert in der Dokumentation. Die Verwendung nicht vertrauenswürdiger Werte kann zur Umgehung von Ratenbegrenzern, zur Umgehung von Zugriffskontrollen, zu Speichererschöpfung oder zu anderen negativen Sicherheits- und Verfügbarkeitsfolgen führen (MDN). Alle vier haben dieselbe Ursache: Eine Entscheidung stützt sich auf einen Wert, den die Gegenseite bestimmt. Die Schwachstellenklassifikation von MITRE führt genau dieses Muster als eigene Klasse – ein Produkt hat zwei verschiedene Quellen derselben Information und nutzt die Quelle, die weniger Prüfmöglichkeit bietet, weniger vertrauenswürdig oder weniger widerstandsfähig gegen Angriffe ist (MITRE).
Wie oft dieses Muster auffällt, lässt sich zählen. Eine Volltextsuche nach X-Forwarded-For in der Schwachstellendatenbank NVD liefert 195 Einträge (NVD, Abruf am 7. September 2026). Das ist keine Aussage über einzelne Produkte, sondern über eine Fehlerart, die seit Jahren wiederkehrt. Besonders unangenehm wird sie bei der Ratenbegrenzung: Wer je Adresse zählt und dafür einen frei setzbaren Wert nimmt, sieht so viele verschiedene Besucher, wie die Gegenseite Adressen erfindet – die Begrenzung greift dann rechnerisch, praktisch aber nicht. Was eine Ratenbegrenzung leisten soll und woran sich ihr Zustand ablesen lässt, steht im Beitrag zu Ratenbegrenzung und Statuscode 429.
Eine Vertrauensgrenze ist eine Einstellung, die bei jedem Umbau der Infrastruktur ungültig werden kann: eine zusätzliche Schicht davor, ein Wechsel des Rechenzentrums, ein neuer Adressbereich. Sie gehört deshalb in eine wiederkehrende Prüfung. Der Security-Scan nimmt die ausgelieferten Kopfzeilen einer Seite regelmäßig auf und macht Abweichungen sichtbar, statt sie bis zum nächsten Vorfall liegen zu lassen.
Forwarded: der standardisierte Weg und seine Tücken
Seit Juni 2014 gibt es eine standardisierte Fassung dieses Gedankens. RFC 7239 definiert das Feld Forwarded, das neben der Adresse auch die angesprochene Gegenstelle, den Protokollnamen und den ursprünglichen Host in einem einzigen Feld führen kann. MDN weist darauf hin, dass diese standardisierte Fassung deutlich seltener eingesetzt wird als X-Forwarded-For (MDN). An der Grundfrage ändert der Standard nichts, und er sagt das selbst: Auf das Feld Forwarded kann man sich nicht verlassen, weil es auf dem Weg zum Server von jedem Knoten verändert werden kann, versehentlich oder in böser Absicht, einschließlich des Clients, der die Anfrage stellt (RFC 7239).
Eine Anfrage kann mehrere X-Forwarded-For-Kopfzeilen enthalten. Die Adressen darin müssen als eine einzige Liste behandelt werden, beginnend mit der ersten Adresse der ersten Kopfzeile bis zur letzten Adresse der letzten Kopfzeile (MDN). Wer nur eine davon liest, liest an der Kette vorbei.
RFC 7239 hält fest, dass IPv6-Adressen in X-Forwarded-For weder in Anführungszeichen noch in eckigen Klammern stehen müssen, in Forwarded dagegen beides tragen (RFC 7239). Ein Auswerter, der eckige Klammern voraussetzt, verwirft dieselbe Adresse je nach Feld.
Statt einer Adresse erlaubt RFC 7239 eine verdeckte Kennung mit vorangestelltem Unterstrich, etwa for=_hidden (RFC 7239). Eine Auswertung muss solche Einträge als das behandeln, was sie sind: kein Messwert, kein Nullwert, sondern eine bewusst zurückgehaltene Angabe.
Beim Übersetzen von X-Forwarded-For nach Forwarded wird jedem Element ein for= vorangestellt; aus X-Forwarded-For: 192.0.2.43, 2001:db8:cafe::17 wird Forwarded: for=192.0.2.43, for="[2001:db8:cafe::17]" (RFC 7239). Die Schreibweise ändert sich, das Vertrauensproblem bleibt.
Zwei weitere Hinweise aus dem Standard betreffen den Betrieb. Erstens soll das Feld nicht von Ursprungsservern oder Zwischenstationen in Antwortnachrichten kopiert werden, weil es die gesamte Proxy-Kette gegenüber dem Client offenlegen kann (RFC 7239). Zweitens ist in Hosting-Umgebungen darauf zu achten, die Methode TRACE nicht zuzulassen, wenn Forwarded im Einsatz ist, weil das Feld sonst im Antwortkörper erscheint (RFC 7239). Beides lässt sich in wenigen Minuten prüfen – und beides fällt ohne Prüfung dauerhaft nicht auf.
Die Einstellung im Webserver
Bei nginx übernimmt das Modul ngx_http_realip_module diese Aufgabe. Die Anweisung set_real_ip_from definiert vertrauenswürdige Adressen, von denen bekannt ist, dass sie korrekte Ersatzadressen senden (nginx). real_ip_header legt fest, welches Feld gelesen wird; die Vorgabe lautet X-Real-IP und nicht X-Forwarded-For (nginx). Und real_ip_recursive steht in der Vorgabe auf off (nginx): Ohne rekursive Suche wird die ursprüngliche Adresse durch die letzte Adresse im Feld ersetzt, mit rekursiver Suche durch die letzte nicht vertrauenswürdige Adresse (nginx). Genau dieser Unterschied entscheidet bei zwei eigenen Schichten darüber, ob die interne oder die externe Adresse im Protokoll landet.
- Beide Anweisungen gehören zusammen:
set_real_ip_fromohne passendesreal_ip_headerliest ein anderes Feld, als Sie meinen. Setzen und prüfen Sie die beiden gemeinsam. - Rekursion bewusst wählen: Bei genau einer eigenen Schicht reicht die Vorgabe. Bei zwei oder mehr eigenen Schichten braucht es
real_ip_recursive onund die Adressbereiche aller Schichten. - Die ursprüngliche Adresse bleibt lesbar: nginx hält sie in
$realip_remote_addrvor (nginx). Wer beide Werte protokolliert, kann später belegen, welcher Sprung welchen Wert geliefert hat. - Apache trägt dieselbe Warnung: Es sei entscheidend, dieses Verhalten nur für Zwischenstationen zu aktivieren, denen dieser Server vertraut, da es für den entfernten Nutzeragenten trivial sei, sich als ein anderer auszugeben (Apache HTTP Server). Die zugrunde liegende Adresse der Verbindung bleibt dort über
%{c}aerreichbar.
Auswertung im Code: von rechts nach links
Ersetzt der Webserver die Adresse bereits, hat die Anwendung nichts weiter zu tun. Steht der Header dagegen unverändert an, gilt die Regel: von rechts beginnen, die eigenen hinterlegten Adressen überspringen, den ersten fremden Eintrag nehmen. Eine verbreitete Sekundärquelle zum Thema ergänzt dazu einen Hinweis, der in der Praxis regelmäßig fehlt: Beim Auswerten des rechten Randes sei die letzte Instanz der Kopfzeile zu verwenden (adam-p.ca, 2022). Und weiter: Eine Kopfzeile, die nicht ausdrücklich vom eigenen Reverse Proxy gesetzt werde, sei nicht vertrauenswürdig (adam-p.ca, 2022). Das betrifft auch die bequemen Felder mit Namen wie X-Real-IP: Sie sind genau so viel wert wie die Stelle, die sie geschrieben hat.
from ipaddress import ip_address, ip_network
EIGENE_PROXYS = [ip_network(n) for n in ("10.0.0.0/8", "192.0.2.0/24")]
def gehoert_uns(adresse):
return any(adresse in netz for netz in EIGENE_PROXYS)
def besucher_ip(header_werte, gegenstelle):
"""Erste fremde Adresse von rechts. None heißt: nicht ermittelt."""
if not gehoert_uns(ip_address(gegenstelle)):
return None # direkt erreichbar: kein Wert belastbar
kette = []
for wert in header_werte: # alle Kopfzeilen in Eingangsreihenfolge
for teil in wert.split(","):
try:
kette.append(ip_address(teil.strip()))
except ValueError:
kette.append(None) # kein gültiger Eintrag
for adresse in reversed(kette):
if adresse is None:
return None # ab hier ist die Kette unlesbar
if not gehoert_uns(adresse):
return adresse
return None
# Aufruf: header_werte sind ALLE X-Forwarded-For-Kopfzeilen der Anfrage
print(besucher_ip(["203.0.113.7, 198.51.100.4", "192.0.2.55"], "192.0.2.55"))
Drei Eigenschaften machen diese Umsetzung belastbar. Erstens liefert sie None zurück, statt eine Adresse zu erfinden, wenn nichts Belastbares vorliegt – ein nicht ermittelter Wert ist kein Nullwert und gehört nicht als Zahl in eine Auswertung. Zweitens bricht sie ab, sobald von rechts ein ungültiger Eintrag auftaucht, denn ab dieser Stelle ist die Kette nicht mehr lesbar. Drittens prüft sie zuerst, ob die Gegenstelle überhaupt einer der eigenen Proxys ist; ist sie es nicht, kam die Anfrage direkt an und der Header hat keine Aussagekraft. Wie sich dieselbe Denkweise auf Verkehr aus Rechenzentren und automatisierte Zugriffe anwenden lässt, steht im Beitrag zum Filtern von Bot-Verkehr.
Was das für Reichweite, Herkunft und Bot-Erkennung bedeutet
In der Reichweitenmessung wirkt eine falsche Adresse anders als in der Zugriffskontrolle: Es fällt niemand aus, es sieht nur alles plausibel aus. Land und Region werden aus der Adresse abgeleitet; steht dort der eigene Load Balancer, sammelt sich der gesamte Verkehr an dessen Standort. Steht dort ein frei gesetzter Wert, verteilt er sich über beliebige Länder. Beides erzeugt eine Karte, die niemand anzweifelt, weil sie gefüllt aussieht. Die IP-Adresse ist dabei kein technisches Detail am Rand: Die Datenschutz-Grundverordnung zählt IP-Adressen ausdrücklich zu den Online-Kennungen, die natürlichen Personen zugeordnet werden können (DSGVO, Erwägungsgrund 30). Was daraus für die Ausgestaltung einer Messung folgt, steht im Beitrag zu datenschutzkonformer Website-Analyse.
- Herkunft: Eine Länderkarte ist nur so gut wie die Adresse, aus der sie abgeleitet wurde. Steht dort der eigene Sprung, zeigt sie den eigenen Standort und keinen Besucher.
- Wiederkehr: Verfahren, die Sitzungen ohne Cookie zusammenführen, nutzen die Adresse als einen Baustein. Eine Sammeladresse führt beliebig viele Besucher zusammen, eine erfundene trennt einen einzelnen Besucher beliebig oft auf.
- Bot-Erkennung: Ob eine Anfrage aus einem Rechenzentrum kommt, entscheidet sich an der Netzklasse der Adresse. Mit dem falschen Eintrag bewertet das Verfahren die Netzklasse des eigenen Proxys. Umgekehrt gilt dieselbe Vorsicht für den Abruf der Suchmaschine: Ob er echt ist, entscheidet die Auflösung des Namens, nicht die Kennung — nachzulesen im Beitrag zum Erkennen von Cloaking.
- Auskunft: Wird zu einer Person Auskunft verlangt, muss nachvollziehbar sein, welche Adresse gespeichert wurde und warum. Wie eine solche Anfrage sauber beantwortet wird, steht im Beitrag zum Auskunftsrecht nach Artikel 15.
Eine Adresse, die nicht aus dem eigenen Teil der Kette stammt, ist kein Messwert, sondern eine Angabe der Gegenseite. Dieser Unterschied entscheidet, ob eine Auswertung eine Entscheidungsgrundlage ist oder eine Behauptung mit Zeitstempel.
— XICTRAQ
Einmal einstellen reicht nicht
Eine Vertrauensgrenze ist eine Momentaufnahme der Infrastruktur. Sie verliert ihre Gültigkeit, wenn eine Schicht davorgesetzt wird, wenn ein Adressbereich wechselt oder wenn ein Auslieferungsnetz aktiviert wird, das bislang nicht in der Kette stand. Nichts davon erzeugt eine Fehlermeldung: Die Anwendung läuft weiter, die Protokolle füllen sich weiter, nur der Bezug stimmt nicht mehr. Deshalb gehört neben die Einstellung eine wiederkehrende Gegenprobe mit einem bekannten falschen Wert – genau das leistet der letzte Befehl im Kasten weiter oben. Wie sich solche Prüfungen vor Lastspitzen einplanen lassen, steht im Beitrag zu Lastspitzen im vierten Quartal.
Für die laufende Messung heißt das: Die Adresse, aus der Herkunft, Netzklasse und Bot-Bewertung abgeleitet werden, sollte aus dem eigenen Teil der Kette stammen und als solche kenntlich sein. XICTRAQ wertet Aufrufe cookielos aus und leitet Land, Netzklasse und Bot-Bewertung aus derselben Adresse ab, sodass eine Abweichung sichtbar wird, statt sich still über alle Berichte zu verteilen – siehe Cookieless Analytics und die Übersicht der Tarife. Die Vertrauensgrenze selbst bleibt dabei Ihre Entscheidung; ein Werkzeug kann sie sichtbar halten, aber nicht für Sie ziehen.
Dieser Beitrag stützt sich auf: MDN (X-Forwarded-For, Abschnitte zu Sicherheit, Auswertung und Adressauswahl), RFC 7239 (Forwarded HTTP Extension, Abschnitte 6.3, 7.4, 8.1 und 8.2), nginx (ngx_http_realip_module), Apache HTTP Server (mod_remoteip), MITRE (CWE-348, Use of Less Trusted Source), NVD (Volltextsuche nach X-Forwarded-For), Web Almanac 2025 (Kapitel CDN, Abbildungen 15.1 und 15.3), die Datenschutz-Grundverordnung (Erwägungsgrund 30) sowie als Sekundärquelle einen Fachbeitrag von adam-p.ca aus dem Jahr 2022. Die genannten Werte sind Stichtagswerte und können sich ändern; jede Zahl ist im Beitrag mit ihrer Quelle ausgewiesen.
Welchen Eintrag aus X-Forwarded-For soll ich nehmen?
Warum steht in meinem Protokoll die Adresse des Load Balancers?
set_real_ip_from benennt die vertrauenswürdigen Adressen, von denen bekannt ist, dass sie korrekte Ersatzadressen senden (nginx). Ohne diese Angabe bleibt die interne Adresse stehen.Kann ein Besucher den Header wirklich selbst setzen?
Was ist der Unterschied zwischen X-Forwarded-For und Forwarded?
Reicht real_ip_recursive on als Einstellung?
off; ohne rekursive Suche ersetzt nginx die ursprüngliche Adresse durch die letzte Adresse im Feld, mit rekursiver Suche durch die letzte nicht vertrauenswürdige Adresse (nginx). Sind nicht alle eigenen Schichten über set_real_ip_from hinterlegt, endet die rekursive Suche an der erstbesten unbekannten Adresse. Die ursprüngliche Adresse bleibt in $realip_remote_addr erhalten und eignet sich als Gegenprobe (nginx).