Ein KI-Agent auf einer Cloud-VM tritt mit einer Datacenter-IP ins Netz. Websites sehen diese IP vor allem anderen, also bekommt der Agent Ergebnisse für das falsche Land, läuft in Rate-Limits, die er nie verursacht hat, oder landet in einer CAPTCHA-Schleife, aus der er nicht herauskommt. Eine gute Proxy-Einrichtung für KI-Agenten löst das Egress-Problem in der Browserkonfiguration, nie im Prompt. Mehrstufige Aufgaben behalten von Anfang bis Ende eine Sticky-IP, parallele Abrufe rotieren. Im Folgenden finden Sie funktionierende Konfigurationen für browser-use, Crawl4AI und einen MCP-Server, dazu Kostenkontrollen und die häufigsten Fehlerbilder.
Warum die Proxy-Einrichtung für KI-Agenten wichtiger ist als für Skripte
Agenten brauchen einen Proxy aus denselben drei Gründen wie Scraper: Geografie, Rate-Limits und IP-Reputation. Sie brauchen ihn dringender, weil sie unbeaufsichtigt laufen und selbst über Wiederholungen entscheiden. Ein blockiertes Skript bricht ab. Ein blockierter Agent versucht es erneut, formuliert um, öffnet einen neuen Tab und versucht es wieder – und jeder Versuch kostet Bandbreite und LLM-Tokens.
- Geografie. Preise, Suchergebnisse, Lagerbestände und rechtliche Hinweise ändern sich je nach Land. Ein Agent, der für einen deutschen Kunden Preise von einer VM in US-East vergleicht, sammelt die falschen Daten. Länder-Targeting im Proxy-Benutzernamen behebt das, ohne die Logik des Agenten anzufassen. Wie Geo-Targeting-Proxies funktionieren erklärt die Mechanik.
- Reputation von Cloud-Egress. Die Adressbereiche von AWS, GCP, Azure und Hetzner sind öffentlich, und IP-Reputationsdienste bewerten sie als Hosting, nicht als Endkundenzugang. Viele Websites fordern diese Bereiche sofort heraus oder drosseln sie, egal wie die Anfrage aussieht. Unsere Analyse zur IP-Reputation erklärt, wie diese Bewertung funktioniert.
- Geteilte Rate-Limits. Mehrere Agenten hinter einem NAT-Gateway teilen sich eine öffentliche IP. Jeder für sich ist höflich, zusammen wirken sie wie ein aggressiver Client.
Was ein Proxy nicht löst: Eine Residential-IP lässt einen automatisierten Browser nicht menschlich aussehen. Verhaltens- und Fingerprint-Erkennung funktionieren weiterhin, wie unser Begleitartikel Wie Websites KI-Browser-Agenten erkennen zeigt. Nutzen Sie Proxies, um korrekte, geografisch passende Daten zu erhalten und die Last fair zu verteilen. Nutzen Sie sie nicht, um eine Website zu umgehen, die Automatisierung ausdrücklich ausgeschlossen hat.
Sticky Sessions vs. Rotation: pro Aufgabe entscheiden, nicht pro Projekt
Faustregel: Trägt die Aufgabe Zustand, fixieren Sie die IP; wenn nicht, rotieren Sie. Ein Ablauf aus Login → Navigation → In den Warenkorb → Checkout muss von Anfang bis Ende eine Exit-IP behalten, weil viele Websites Session-Cookies, CSRF-Tokens oder Risikoscores an die IP binden, die die Sitzung eröffnet hat. Wechselt die IP mitten im Ablauf, werden Sie oft ausgeloggt oder erneut herausgefordert. Bei unabhängigen Seitenabrufen ist es umgekehrt: Wer sie über mehrere IPs verteilt, bleibt pro IP unter den Limits.
Bei einem Backconnect-Gateway wechseln Sie den Modus über den Benutzernamen. USERNAME-sid-abc123-ttl-10m behält dieselbe Exit-IP, solange Sie diese Session-ID wiederverwenden. Ein schlichtes USERNAME rotiert. USERNAME-country-US fixiert das Land. Der Leitfaden zu Sticky vs. rotierenden Sessions behandelt Sitzungslaufzeiten ausführlicher.
| Aufgabe des Agenten | IP-Modus | Warum |
|---|---|---|
| Einloggen, dann im Konto handeln | Sticky, eine Session-ID pro Konto | Session-Cookies und Risikoscores hängen oft an der IP, die sich eingeloggt hat. Ein IP-Wechsel mitten in der Sitzung sieht nach Kontoübernahme aus. |
| Mehrseitiges Formular oder Checkout-Test | Sticky für den gesamten Ablauf | CSRF-Tokens und Warenkorbstatus bleiben erhalten. Ein fehlgeschlagener Schritt lässt sich von derselben IP wiederholen. |
| Recherche-Agent, der 50 unzusammenhängende Seiten liest | Rotierend | Kein gemeinsamer Zustand. Verteilte Anfragen vermeiden Drosselung pro IP auf einzelnen Domains. |
| Bulk-Crawl für RAG-Ingestion | Rotierend oder ein kleiner Pool von Sticky-Slots | Rotation verteilt die Last. Sticky-Slots lassen eine JS-lastige Website ihre Cookies über einige Seiten behalten. |
| Geo-Vergleich (dieselbe URL, mehrere Länder) | Länder-Targeting, Rotation innerhalb eines Landes | Sie wollen eine Antwort pro Markt, nicht eine pro IP. |
| Überwachung einer Seite alle N Minuten | Rotierend | Wiederholte Aufrufe von einer IP bilden ein Muster. Rotation hält die Last pro IP niedrig. |
browser-use: Proxy-Konfiguration in der aktuellen API
browser-use nimmt bei Browser (einem Alias von BrowserSession) das Argument proxy=ProxySettings(...) mit den Feldern server, username, password und bypass entgegen. Die Zugangsdaten gehen an den Browser und nie in den LLM-Kontext. Intern startet browser-use Chromium mit --proxy-server und beantwortet die Auth-Challenge des Proxys über CDP (Fetch.authRequired), Zugangsdaten in der URL sind also nie nötig. Achten Sie auf den Importpfad: ProxySettings liegt in browser_use.browser, nicht im Paket-Root (siehe die Referenz der Browser-Parameter und profile.py).
import asyncio
import os
import uuid
from browser_use import Agent, Browser, ChatOpenAI
from browser_use.browser import ProxySettings
PROXY_USER = os.environ["PROXYHAT_USER"]
PROXY_PASS = os.environ["PROXYHAT_PASS"]
async def run_checkout_test() -> None:
# Eine Sticky-Session-ID für den ganzen Ablauf, damit Login -> Warenkorb -> Checkout eine Exit-IP behält.
session_id = uuid.uuid4().hex[:12]
browser = Browser(
headless=True,
proxy=ProxySettings(
server="http://gate.proxyhat.com:8080",
username=f"{PROXY_USER}-sid-{session_id}-ttl-10m",
password=PROXY_PASS,
bypass="localhost,127.0.0.1",
),
# Hält den Agenten auf der Testseite; begrenzt auch, wo sensitive_data eingetippt werden kann.
allowed_domains=["https://staging.example-shop.com"],
# Chromium-Switch: Bilder gar nicht erst herunterladen.
args=["--blink-settings=imagesEnabled=false"],
)
agent = Agent(
task="Log in with x_user / x_pass, add the cheapest USB-C cable to the cart, "
"stop at the payment page and report the order total.",
llm=ChatOpenAI(model=os.environ["OPENAI_MODEL"]),
browser=browser,
# Das Modell sieht die Platzhalter x_user / x_pass, nie die Werte.
sensitive_data={
"https://staging.example-shop.com": {
"x_user": os.environ["SHOP_USER"],
"x_pass": os.environ["SHOP_PASS"],
}
},
use_vision=False, # keine Screenshots in jedem Schritt: weniger Tokens
max_failures=2, # Standard ist 5; jeder Fehlschlag bedeutet eine weitere Runde Seitenaufrufe
)
history = await agent.run(max_steps=25) # Standard ist 500
print(history.final_result())
asyncio.run(run_checkout_test())
Hinweise zu diesem Muster:
- Nutzen Sie bei authentifizierten Proxies den HTTP-Port. Die Dokumentation des Chromium-Netzwerkstacks hält fest, dass für SOCKSv5 keine Authentifizierungsmethoden unterstützt werden;
socks5://…:1080mit Zugangsdaten scheitert also in jedem Chromium-basierten Agenten. Verwenden Siehttp://gate.proxyhat.com:8080. - Proxy-Zugangsdaten sind keine
sensitive_data.sensitive_dataist für Werte gedacht, die der Agent in Seiten eintippen muss. Proxy-Zugangsdaten gehören inProxySettings, das das Modell nie sieht. Fügen Sie keine Proxy-URL in dentask-String ein, „damit der Agent Bescheid weiß“. allowed_domainsist wichtiger, als es aussieht. browser-use gibt eine Warnung aus, wenn Siesensitive_dataübergeben, ohne die Domains einzuschränken, weil eine Seite mit Prompt-Injection den Agenten sonst dazu bringen könnte, Geheimnisse anderswo einzutippen.- Der Cloud-Modus ist ein anderer Egress.
Browser(use_cloud=True)führt den Browser in der Browser Use Cloud mit deren eigenen Proxies aus. IhreProxySettingsgelten für den lokalen oder percdp_urlangebundenen Browser, den Sie selbst starten. - Standard-Erweiterungen sparen Bandbreite.
enable_default_extensionsist standardmäßig aktiv und lädt uBlock Origin Lite sowie einen Cookie-Banner-Handler, sodass die meisten Anzeigen und Tracker gar nicht erst Ihren Traffic belasten.
Crawl4AI: Proxies pro Crawl, Rotation und Sticky Sessions
In Crawl4AI setzen Sie den Proxy pro Crawl in CrawlerRunConfig, entweder als proxy_config oder über eine proxy_rotation_strategy. Die Proxy-Dokumentation markiert das alte proxy-Argument von BrowserConfig als veraltet und empfiehlt, Proxies pro Anfrage zu konfigurieren. Für Sticky-Verhalten fixiert proxy_session_id einen Proxy aus der Strategie für jeden Crawl, der diese ID teilt.
Ein Backconnect-Gateway ist ein einzelner Host, „eine Liste von Proxies“ bedeutet also eine Liste von Session-Benutzernamen. Jede Session-ID ist ein Slot mit eigener Exit-IP, und Round-Robin verteilt die URLs auf die Slots:
import asyncio
import os
import uuid
from crawl4ai import (AsyncWebCrawler, BrowserConfig, CacheMode,
CrawlerRunConfig, ProxyConfig, RoundRobinProxyStrategy)
USER = os.environ["PROXYHAT_USER"]
PASS = os.environ["PROXYHAT_PASS"]
def slot() -> ProxyConfig:
# Eine Sticky-Session pro Slot, damit jeder Slot eine Exit-IP behält.
sid = uuid.uuid4().hex[:10]
return ProxyConfig(
server="http://gate.proxyhat.com:8080",
username=f"{USER}-sid-{sid}-ttl-10m",
password=PASS,
)
strategy = RoundRobinProxyStrategy([slot() for _ in range(8)])
browser_cfg = BrowserConfig(
headless=True,
text_mode=True, # versucht, Bilder und andere schwere Inhalte abzuschalten
avoid_ads=True, # blockiert gängige Werbe-/Tracker-Domains
)
bulk_cfg = CrawlerRunConfig(
proxy_rotation_strategy=strategy, # nächster Slot für jede URL
cache_mode=CacheMode.BYPASS,
check_robots_txt=True,
max_retries=1,
)
async def main(urls: list[str]) -> None:
async with AsyncWebCrawler(config=browser_cfg) as crawler:
results = await crawler.arun_many(urls, config=bulk_cfg)
for r in results:
print(r.url, r.success, len(r.markdown or ""))
# Zustandsbehaftete Abfolge: derselbe Proxy-Slot UND dieselbe Browserseite für jeden Schritt.
step_cfg = bulk_cfg.clone(proxy_session_id="acct-42", session_id="acct-42")
await crawler.arun("https://example.com/login", config=step_cfg)
await crawler.arun("https://example.com/account/orders", config=step_cfg)
asyncio.run(main(["https://example.com/a", "https://example.com/b"]))
Zwei Einstellungen werden leicht verwechselt. session_id behält dieselbe Browserseite über arun()-Aufrufe hinweg, proxy_session_id denselben Proxy. Ein zustandsbehafteter Ablauf braucht beides. Für Rotation mit fixiertem Land statt Sticky-Slots nutzen Sie eine einzelne ProxyConfig mit dem Benutzernamen USERNAME-country-US.
Eine Falle durch Wiederholungsverstärkung: proxy_config akzeptiert auch eine Liste von Fallback-Proxies, und die Retry-Schleife in async_webcrawler.py geht bei jedem Versuch die ganze Liste durch. Eine blockierte URL kann daher bis zu (max_retries + 1) × len(list)-mal abgerufen werden. Mit 10 Fallbacks und max_retries=3 sind das 40 vollständige Seitenaufrufe für eine URL, die ohnehin nie geladen hätte. Halten Sie die Liste kurz und setzen Sie max_retries bewusst.
Einem MCP-Assistenten Webzugriff über einen Proxy geben
Sie haben zwei Möglichkeiten: Microsofts Playwright-MCP-Server mit einem Proxy in der Konfiguration betreiben oder ein kleines MCP-Tool schreiben, das über den Proxy abruft und Text zurückgibt. Nehmen Sie die erste, wenn der Assistent klicken und tippen muss, die zweite, wenn er nur Seiten lesen soll – das ist pro Aufruf deutlich günstiger.
Option 1: Playwright MCP mit authentifiziertem Proxy
@playwright/mcp kennt die Flags --proxy-server und --proxy-bypass (README). Für einen Proxy mit Zugangsdaten nutzen Sie stattdessen die JSON-Konfigurationsdatei. Deren browser.launchOptions wird direkt an Playwright durchgereicht, dessen Proxy-Option separate Felder username und password hat:
{
"browser": {
"browserName": "chromium",
"isolated": true,
"launchOptions": {
"headless": true,
"proxy": {
"server": "http://gate.proxyhat.com:8080",
"username": "USERNAME-sid-mcp01-ttl-10m",
"password": "PASSWORD"
}
}
},
"capabilities": ["core"]
}
npx @playwright/mcp@latest --config ./playwright-mcp.json --image-responses omit
Halten Sie diese Datei aus dem Repository heraus und nur für den Benutzer lesbar, der den Server ausführt. Die Session-ID bedeutet hier, dass die gesamte Assistenten-Sitzung eine IP teilt, was zu „einloggen und X erledigen“ passt. Starten Sie mit einer neuen ID neu, wenn Sie eine frische IP wollen. --image-responses omit verhindert, dass Screenshots an das Modell zurückgehen. "isolated": true (das Flag --isolated) hält das Profil im Speicher, sodass keine Cookies zwischen Aufgaben durchsickern. Die README weist außerdem darauf hin, dass --allowed-origins und --blocked-origins keine Sicherheitsgrenze sind. Betrachten Sie sie als Hygiene, nicht als Schutz.
Option 2: ein minimales Fetch-Tool über Proxy mit dem offiziellen MCP Python SDK
Version 2 des Python SDK hat FastMCP in MCPServer umbenannt (Migrationsleitfaden). Für v1 geschriebener Code mit from mcp.server.fastmcp import FastMCP braucht den neuen Import. Hier ein reines Lese-Tool, das über das Gateway abruft, die Antwortgröße begrenzt und die Proxy-URL nie in seine Ausgabe schreibt:
import os
import re
import httpx
from mcp.server import MCPServer
mcp = MCPServer("proxied-fetch")
_USER = os.environ["PROXYHAT_USER"]
_PASS = os.environ["PROXYHAT_PASS"]
_MAX_BYTES = 1_500_000
_COUNTRY = re.compile(r"^[A-Za-z]{2}$")
_SESSION = re.compile(r"^[A-Za-z0-9]{1,24}$")
def _proxy(country: str | None, session: str | None) -> str:
user = _USER
# Vom Modell gelieferte Werte validieren: Sie landen im Proxy-Benutzernamen.
if session:
if not _SESSION.match(session):
raise ValueError("session must be 1-24 alphanumerics")
user = f"{_USER}-sid-{session}-ttl-10m"
elif country:
if not _COUNTRY.match(country):
raise ValueError("country must be a 2-letter code")
user = f"{_USER}-country-{country.upper()}"
return f"http://{user}:{_PASS}@gate.proxyhat.com:8080"
@mcp.tool()
async def fetch_page(url: str, country: str | None = None, session: str | None = None) -> str:
"""Fetch a public http(s) page through a residential proxy and return its text.
country: optional 2-letter exit country. session: optional ID to reuse one exit IP.
"""
if not url.startswith(("http://", "https://")):
return "error: only http(s) URLs are allowed"
try:
async with httpx.AsyncClient(proxy=_proxy(country, session), timeout=30,
follow_redirects=True) as client:
async with client.stream("GET", url) as resp:
body = b""
async for chunk in resp.aiter_bytes():
body += chunk
if len(body) > _MAX_BYTES:
break
text = body.decode(resp.encoding or "utf-8", errors="replace")
return f"HTTP {resp.status_code} {resp.url}\n\n{text[:200_000]}"
except ValueError as e:
return f"error: {e}"
except httpx.HTTPError as e:
# Nur den Exception-Typ: Manche Meldungen können die Proxy-URL enthalten.
return f"error: fetch failed ({type(e).__name__})"
if __name__ == "__main__":
mcp.run(transport="stdio")
Registrieren Sie es in Ihrem MCP-Client als stdio-Server. Übergeben Sie PROXYHAT_USER und PROXYHAT_PASS über den env-Block des Clients, nie über Tool-Argumente. In Claude Code sieht das zum Beispiel so aus: claude mcp add --env PROXYHAT_USER=… --env PROXYHAT_PASS=… --transport stdio proxied-fetch -- python server.py. Lassen Sie --transport zwischen dem letzten --env und dem Servernamen stehen, sonst liest die CLI den Namen als weiteres env-Paar. Die Regex-Prüfungen haben ihren Grund: Eine Seite mit Prompt-Injection kann das Modell dazu bringen, das Tool mit seltsamen Argumenten aufzurufen, und ungeprüfte Eingaben würden beliebigen Text in Ihren Proxy-Benutzernamen schreiben. Für Seiten, die JavaScript brauchen, nehmen Sie Option 1 oder – wenn Sie die Browserschicht selbst bauen – unseren Leitfaden zu rotierenden Proxies mit Playwright.
Kostenkontrolle: Agenten verbrauchen Bandbreite anders als Skripte
Residential-Traffic wird pro Gigabyte abgerechnet, und ein Agent lädt vollständige Seiten, oft mehrmals pro Schritt. Diese Maßnahmen bringen am meisten, ungefähr in dieser Reihenfolge:
- Laden Sie nichts, was das Modell nicht liest. Bilder, Videos, Schriften und Werbeskripte machen den Großteil einer modernen Seite aus. Nutzen Sie
text_mode=Trueundavoid_ads=Truein Crawl4AI, den SwitchimagesEnabled=falseoder uBlock in browser-use und ein Fetch-Tool statt eines Browsers, wenn die Seite serverseitig gerendert wird. - Begrenzen Sie Wiederholungen an zwei Stellen. Framework-Retries (
max_failures,max_retries) und Neuplanung auf Agentenebene multiplizieren sich. Ein Agent, der nach einem 403 „einen anderen Ansatz versucht“, lädt oft nur dieselbe blockierte Seite neu. Setzen Siemax_stepsso niedrig, dass ein festgefahrener Lauf billig stirbt. - Verwenden Sie Kontexte in zustandsbehafteten Abläufen wieder. Ein neuer Browser-Kontext bedeutet frische Cookies, eine frische Challenge und einen erneuten Download jedes gecachten Assets. Halten Sie innerhalb einer Aufgabe den Kontext (und die Sticky-IP) am Leben.
- Stoppen Sie bei Sperrsignalen. Behandeln Sie eine CAPTCHA-Seite, einen 403 eines Bot-Management-Anbieters oder einen 429 als Endzustand, der an einen Menschen gemeldet wird. Lassen Sie den Agenten sich nicht per Wiederholung daran vorbeiprobieren. Unser Leitfaden zum Umgang mit CAPTCHAs begründet, warum Zurückhaltung sinnvoll ist.
- Messen Sie pro Aufgabe, nicht pro Monat. Loggen Sie Bytes pro Agentenlauf. Ein plötzlicher Sprung bedeutet meist eine Schleife, keinen größeren Auftrag.
Fehlerbilder, für die Sie vorsorgen sollten
Die meisten Proxy-Vorfälle bei Agenten haben eine von vier Ursachen: eine CAPTCHA-Schleife, Wiederholungsverstärkung, eine Sitzung, die unbemerkt die IP wechselt, oder Zugangsdaten, die in den Modellkontext gelangen.
- CAPTCHA-Schleifen. Agenten mit Vision versuchen, eine Challenge zu „lösen“, scheitern, laden neu und versuchen es wieder. Erkennen Sie Challenge-Seiten (bekannte iframe-Hosts, Titel wie „Just a moment…“) und beenden Sie den Lauf. Challenges in großem Stil zu beantworten gehört nicht zu einer legitimen Pipeline, und wiederholte Fehlschläge von einer Residential-IP schaden deren Reputation für alle, die sie nutzen.
- Wiederholungsverstärkung. Oben behandelt: Framework-Retries × Proxy-Fallback-Listen × Neuplanung des Agenten. Beobachten Sie die Gesamtzahl der Versuche pro URL, nicht nur die Fehlschläge.
- Unbemerkte Sitzungsabbrüche. Eine Sticky Session endet, wenn ihre TTL abläuft, und kann früher enden, wenn der Upstream-Peer offline geht; die nächste Anfrage geht dann über eine neue IP raus. Leitet eine Anfrage mitten im Ablauf plötzlich auf eine Login-Seite um, gehen Sie davon aus, dass sich die IP geändert hat. Starten Sie den Ablauf mit einer neuen Session-ID neu, statt den Agenten improvisieren zu lassen.
- Zugangsdaten im LLM-Kontext. Alles, was das Modell erreicht, kann in Logs, Traces, Eval-Datensätzen oder einer per Prompt-Injection ausgelösten Exfiltrationsanfrage landen. Bewahren Sie Proxy-Zugangsdaten in Umgebungsvariablen auf, die der Browser oder HTTP-Client liest. Schreiben Sie sie nicht in Aufgabenstrings, System-Prompts oder Tool-Ausgaben, und bereinigen Sie Exception-Meldungen, bevor Sie sie zurückgeben. Die Option
secretsvon Playwright MCP kann bekannte Zeichenketten aus Tool-Antworten schwärzen, doch die eigene README nennt das eine Bequemlichkeitsfunktion, kein Sicherheitsmerkmal.
Bevor Sie dem Agenten die Schuld geben, testen Sie den Proxy für sich: Prüfen Sie die konfigurierte Benutzernamenvariante mit dem Proxy-Checker und bestätigen Sie, dass Exit-Land und IP-Typ Ihren Erwartungen entsprechen. Einen breiteren Vergleich von IP-Typen für Agenten-Workloads bietet Die besten Proxys für KI-Agenten und LLM-Webdatenerhebung.
Was dieses Setup nicht löst
Ein Proxy ändert, woher Traffic kommt, nicht wie er sich verhält. Er bringt einen Agenten nicht durch verhaltensbasierte Boterkennung, und er ist keine Erlaubnis, auf eine Website zuzugreifen, die Automatisierung in ihrer robots.txt oder ihren Nutzungsbedingungen ausschließt. Auch die Erhebung personenbezogener Daten wird dadurch nicht rechtmäßig. Bietet eine Website eine API, einen Feed oder ein Programm für signierte Agenten, sind diese Wege meist günstiger und zuverlässiger als jede Proxy-Konfiguration. Setzen Sie Residential-IPs für das ein, was sie gut können: korrekte Geografie, faire Lastverteilung und keine geerbte Reputation eines Cloud-Adressbereichs.






