Proxy in Python requests konfigurieren: Authentifizierung und Fehler prüfen

11 Min. Lesezeit

Proxy in Python Requests setzen: HTTP und SOCKS5, Login in der URL, Session gegen Umgebung, Timeouts, TLS und die genaue Bedeutung von 407, ProxyError und SSLError.

Dieser Text ist für Leute, die Python Requests über einen Proxy schicken und den Weg prüfen wollen. Die Snippets nutzen Platzhalter und Dummy-Zugangsdaten.

01Proxy in requests einrichten und den Weg prüfen

Die Beispiele folgen der offiziellen Requests-Dokumentation zu Proxys und wurden am 14. Oktober 2026 gegen eine lokale Loopback-Fixture wiederholt: Python 3.14.6, Requests 2.34.2, urllib3 2.8.0, PySocks 1.7.1, TLS-Prüfung an. Das Labor zeigt Protokollverhalten, nicht Geschwindigkeit, Geografie oder die Qualität einer Anbieter-IP.

Jede Request-Methode nimmt ein Dict proxies. Die Schlüssel sind das Schema der Ziel-URL. Die Werte sind die Proxy-URL:

import requests

proxies = {
    "http": "http://203.0.113.10:8080",
    "https": "http://203.0.113.10:8080",
}

session = requests.Session()
session.trust_env = False
r = session.get("https://example.com/", proxies=proxies, timeout=(3.05, 27))
print(r.status_code)
  1. Legen Sie eine Session an und setzen Sie trust_env = False, solange Sie den Proxy selbst wählen.
  2. Tragen Sie HTTP und HTTPS auf denselben Proxy, wenn Ihr Anbieter einen HTTP-Proxy mit CONNECT anbietet.
  3. Setzen Sie immer ein Timeout-Tupel, sonst kann der Aufruf hängen.
  4. Schicken Sie eine kleine GET-Anfrage an einen Endpunkt, den Sie kontrollieren.
  5. Lesen Sie die Antwort und die Proxy-Logs. Ein Browser auf Meine IP ist der Browser-Ausgang, nicht der Python-Weg.

In der Fixture lieferte der direkte GET gegen den lokalen Origin HTTP 200. Derselbe GET über den offenen Loopback-Proxy ebenfalls 200. Der Proxy sah die absolute Form GET http://127.0.0.1:…/ HTTP/1.1. Ohne diesen Trace wissen Sie nur, dass irgendetwas 200 gesagt hat.

Requests akzeptiert in 2.34.2 auch 127.0.0.1:8080 ohne Schema. Der Aufruf kam in der Fixture mit HTTP 200 durch, intern wird http:// ergänzt. Schreiben Sie das Schema trotzdem. Ohne http:// oder socks5:// ist die URL mehrdeutig, sobald SOCKS im Spiel ist.

Ein Schlüssel der Form https://api.intern.example gilt nur für genau diesen Host, nicht für Unterdomänen.

02HTTP-Proxy und HTTPS-Ziel richtig unterscheiden

Der Eintrag "https" zeigt fast immer auf eine http://-Proxy-URL. Das ist Absicht. Requests schickt dem Proxy CONNECT host:443 und spricht TLS erst im Tunnel. In der Fixture stand in den Proxy-Ereignissen CONNECT localhost:50855 HTTP/1.1, während das Ziel https://localhost:…/ war. Das Schema der Ziel-URL ist nicht das Schema des Proxy-Endpunkts.

Schema: Requests über einen HTTP-Proxy – HTTP-Ziel als GET in absoluter Form, HTTPS-Ziel als CONNECT mit TLS im Tunnel, und wie ein fehlender Proxy-Login jeweils aussieht
Schema, kein Messwert: Der Schlüssel im Dict ist das Ziel-Schema, der Wert die Proxy-URL. Request-Zeilen aus der lokalen Loopback-Fixture.

https:// in der Proxy-URL brauchen Sie nur, wenn der Anbieter TLS direkt auf dem Proxy-Port terminiert. Das ist ein anderer Dienst als ein klassischer Forward-Proxy.

HTTP zum Origin geht als Absolute-Form-GET. HTTPS geht als CONNECT. Dieselbe fehlende Anmeldung sieht deshalb anders aus:

Symptom in Requests 2.34.2SchichtWas prüfen
Antwortstatus 407, Body "Proxy Authentication Required"HTTP-Proxy, Absolute-Form-GETBenutzer, Passwort, Prozentkodierung. Keine Exception.
ProxyError, Text enthält Tunnel connection failed: 407CONNECT zum HTTPS-ZielDieselbe Anmeldung, aber am Tunnel. HTTPProxyAuth reicht hier nicht.
Antwortstatus 401 nach gültigem Proxy-LoginZielserverProxy ist durch. Die App am Origin lehnt ab.
ProxyError mit ConnectTimeoutErrorTCP zum ProxyHost, Port, Firewall. Das Timeout gilt für den Connect, nicht für die ganze Anfrage.
ConnectionError mit innerem ReadTimeoutError nach HeadernLesen vom OriginHeader waren da. Der Body kam zu langsam.
SSLError / CERTIFICATE_VERIFY_FAILEDTLS zum Ziel im TunnelCA des Ziels oder des interceptierenden Proxys, nicht verify=False.
ConnectionError, nodename nor servname provided bei socks5://DNS auf dem ClientHostname unauflösbar. socks5h:// schickt den Namen an den Proxy.

Wenn Sie nur wissen wollen, ob ein Endpunkt überhaupt antwortet, reicht der Proxy-Checker. Für die längere Checkliste siehe So prüfen Sie, ob ein Proxy funktioniert. Beides ersetzt kein Python-Trace.

03Benutzername und Passwort sicher übergeben

Die Dokumentation verlangt Basic Auth in der Proxy-URL, mit Prozentkodierung pro Komponente. Sonderzeichen im Passwort gehören nicht roh in die URL. RFC 7617 Abschnitt 2 macht den user-id nach dem Decodieren ungültig, sobald er einen Doppelpunkt enthält. Der erste Doppelpunkt trennt Benutzer und Passwort. Prozentkodierung eines Doppelpunkts im Benutzernamen ändert daran nichts. Das Passwort darf Doppelpunkte enthalten, unser Dummy tut das.

from urllib.parse import quote

user = quote("demo", safe="")
password = quote("p@ss:/#%", safe="")
proxies = {
    "http": f"http://{user}:{password}@203.0.113.10:8080",
    "https": f"http://{user}:{password}@203.0.113.10:8080",
}

Ohne Anmeldung lieferte der lokale Auth-Proxy auf HTTP den Status 407 als normale Antwort. Derselbe fehlende Header auf HTTPS wurde zu requests.exceptions.ProxyError mit OSError('Tunnel connection failed: 407 Proxy Authentication Required'). Nicht jeder 407 sieht gleich aus. Eine Diagnose-Seite zu Status 407 im Browser ist ein anderes Thema als dieser CONNECT-Fall.

Falsche Dummy-Zugangsdaten in der URL, Benutzer demo, Passwort nicht das erwartete, lieferten auf HTTP den Status 407. Der Proxy sah Proxy-Authorization und lehnte ab. Derselbe falsche Login auf HTTPS wurde zu ProxyError mit 407 am CONNECT. Mit den richtigen kodierten Dummy-Daten kamen HTTP und HTTPS-CONNECT auf 200, sobald die Labor-CA für das selbst signierte Ziel gesetzt war.

HTTPProxyAuth ist die Falle, die in Foren immer wieder auftaucht:

from requests.auth import HTTPProxyAuth

auth = HTTPProxyAuth("demo", "p@ss:/#%")
session.get(url, proxies=bare_proxies, auth=auth, timeout=(3.05, 10))

In der Fixture: HTTP-GET mit HTTPProxyAuth war 200. Derselbe auth= auf https://localhost endete wieder in ProxyError mit 407 am CONNECT. urllib3 setzt den Header aus HTTPProxyAuth nicht zuverlässig auf den Tunnel. Die Zugangsdaten gehören in die Proxy-URL.

Entscheidungshilfe: Proxy-Login ohne Zugangsdaten, mit HTTPProxyAuth oder kodiert in der Proxy-URL – Ergebnis für HTTP- und HTTPS-Ziel in der lokalen Fixture
Ergebnisse der lokalen Loopback-Fixture vom 14.10.2026 mit Dummy-Zugangsdaten (Requests 2.34.2, urllib3 2.8.0). Protokolltest, keine Prüfung eines Anbieters.

Loggen Sie die URL nicht. In unseren Rohdaten ist das Passwort durch *** ersetzt. Ein 401 vom Origin bei gültigem Proxy-Login blieb 401. Das ist die App, nicht der Proxy.

04Session, Umgebungsvariablen und trustenv verstehen

Eine Session hält Verbindungspool und Defaults. Die Docs warnen ausdrücklich: Werte in session.proxies können von Umgebungsproxys überschrieben werden, die urllib.request.getproxies() liefert. Issue psf/requests#2018 beschreibt denselben Effekt.

In der Fixture zeigte Session.proxies auf den offenen Loopback-Proxy, während HTTP_PROXY und HTTPS_PROXY auf einen zweiten zeigten. Der GET ohne proxies= am Aufruf landete beim Umgebungsproxy. Dieselbe Session mit proxies= am Aufruf traf den offenen Proxy. Eine zweite Session mit trust_env = False ignorierte die Umgebung und nutzte Session.proxies.

Entscheidungshilfe: welcher Proxy gilt – Session.proxies, HTTP_PROXY/HTTPS_PROXY, proxies= am Aufruf, trust_env = False und NO_PROXY
Fünf Fälle aus der lokalen Fixture. A = Session.proxies, B = Proxy aus der Umgebung. Sonderfall CGI/REQUEST_METHOD steht nur im Text.

Zwei belastbare Muster:

# 1. proxies an jedem Aufruf, wie in den Docs empfohlen
session.get(url, proxies=proxies, timeout=10)

# 2. Umgebung für diese Session abschalten
session.trust_env = False
session.proxies.update(proxies)

trust_env = False liest auch kein .netrc und keine CA-Variablen wie REQUESTS_CA_BUNDLE. Wenn Ihr Unternehmen die CA genau darüber setzt, müssen Sie verify= selbst übergeben.

Ohne proxies= liest Requests http_proxy, https_proxy, no_proxy und all_proxy. In einem normalen Prozess, ohne REQUEST_METHOD, reichte in der Fixture allein HTTP_PROXY/HTTPS_PROXY in Großbuchstaben. Der GET landete beim Umgebungsproxy. CPython urllib.request.getproxies_environment verwirft Großschreibung von HTTP_PROXY für das Schema http, sobald REQUEST_METHOD gesetzt ist, CGI-Schutz gegen einen Proxy:-Header. Dieselbe Fixture mit REQUEST_METHOD=GET und nur Großschreibung ging direkt, der Umgebungsproxy sah nichts. Kleingeschriebenes http_proxy galt auch mit REQUEST_METHOD. curl behandelt HTTP-Proxy-Variablen historisch anders, oft nur kleingeschrieben. Die beiden Welten nicht mischen.

NO_PROXY=127.0.0.1,localhost übersprang den Umgebungsproxy, der GET ging direkt. Dieselbe Session mit explizitem proxies= schickte denselben Host trotzdem durch den Proxy. NO_PROXY gilt für den Weg aus der Umgebung, nicht für ein Dict, das Sie übergeben.

Zugangsdaten in der Shell-Umgebung oder in Git sind ein Risiko. Die Docs sagen das klar. Für Skripte gehören Secrets in den Secret-Store der CI, nicht in export HTTPS_PROXY=http://user:pass@….

05SOCKS5, socks5h und DNS prüfen

SOCKS ist optional. Die Docs verlangen python -m pip install 'requests[socks]'. In der Fixture hing PySocks 1.7.1. Ohne Extra gibt es keinen stillen Fallback auf HTTP.

proxies = {
    "http": "socks5://demo:***@203.0.113.10:1080",
    "https": "socks5://demo:***@203.0.113.10:1080",
}

socks5 löst DNS auf dem Client. socks5h schickt den Hostnamen an den Proxy, analog zu curl. Das steht in den Requests-Docs zu SOCKS. Ein erfolgreicher Request beweist die Auflösung nicht.

Gegen http://127.0.0.1:…/ sah der SOCKS-Server ATYP IPv4, Ziel 127.0.0.1. Gegen http://lab-origin.invalid:…/ mit socks5:// kam ConnectionError / NewConnectionError, errno 8, nodename nor servname provided, or not known. Der Proxy bekam diese Anfrage nicht. Mit socks5h:// kam HTTP 200, der Server notierte ATYP Hostname und lab-origin.invalid. HTTPS nach localhost über socks5h mit Labor-CA war ebenfalls 200, Ziel als Hostname.

Schema: socks5 löst den Hostnamen auf dem Client auf und scheitert vor dem Proxy, socks5h übergibt den Namen an den SOCKS-Proxy
Schema mit Fehlertexten aus der lokalen Fixture (Ziel lab-origin.invalid, PySocks 1.7.1). Ein 200 allein beweist nicht, wo aufgelöst wurde.

Wenn Ihr Anbieter den Namen intern auflösen soll, weil die Exit-Maschine andere Resolver hat als Ihr Laptop, ist socks5h die Variante. socks5 ist richtig, wenn der Client den Namen kennen muss. Der Unterschied HTTP gegen SOCKS5 in der Produktwahl steht in SOCKS5 vs HTTP-Proxys. Protokoll ist nicht IP-Herkunft. Ein Datacenter-HTTP-Proxy und ein Residential-SOCKS5 können denselben Requests-Code füttern und trotzdem völlig verschiedene Exit-Netze haben.

06Timeouts, TLS und Verbindungsfehler eingrenzen

Timeouts in Requests sind kein Gesamtdeadline. Ein Float oder das erste Element des Tupels begrenzt den Connect. Das zweite Element begrenzt die Pause zwischen Bytes beim Lesen. Ohne Timeout wartet Requests unbegrenzt.

Gegen einen lokalen Listener, dessen Accept-Warteschlange voll war, mit timeout=(0.8, 5) kam nicht ConnectTimeout oben. Es kam requests.exceptions.ProxyError, Kette MaxRetryError → urllib3.exceptions.ProxyError → ConnectTimeoutError → TimeoutError. Die Phrase Max retries exceeded beweist keine wiederholten Connects. Der installierte HTTPAdapter hat max_retries.total = 0. urllib3 verpackt den ersten Fehlversuch trotzdem in MaxRetryError.

Ein konfiguriertes urllib3.util.Retry haben wir nicht gemessen. Wenn Sie es setzen, zählt total zusätzliche Versuche, keine Wanduhr. allowed_methods steuert Status- und Read-Retries. POST fehlt dort standardmäßig, Connect-Fehler an POST werden davon nicht ausgeschlossen. Für Status-Retries mit gesetzter Liste kann Retry-After die Pause strecken. Das ist Doku und Default-Objekte, kein Lauf in dieser Fixture.

Ein Origin, der Header sofort schickte und den Body vier Sekunden liegen ließ, mit timeout=(3.05, 1.0), endete als ConnectionError mit innerem ReadTimeoutError und dem Text Read timed out. Nicht als top-level ReadTimeout. Der offene Proxy hatte das GET schon angenommen. Endloses Retry gegen denselben toten Host ändert daran nichts. Rotieren, bis irgendein Exit den Zielstatus 403 umgeht, nein.

TLS bleibt an. SSL-Prüfung ist Standard. Das selbst signierte Laborziel mit verify=True war SSLError / SSLCertVerificationError: self-signed certificate. Mit verify= auf die Labor-CA kam HTTP 200 durch denselben CONNECT. verify=False war kein Fall und ist kein normaler Fix. Wenn ein Anbieter TLS bricht, brauchen Sie dessen CA in verify= oder in einem expliziten Bundle, nicht das Abschalten der Prüfung.

trust_env = False nimmt Ihnen REQUESTS_CA_BUNDLE. Setzen Sie die Datei dann am Aufruf. Interceptierende Firmenproxys sind genau dieser Fall.

07Passenden Endpoint auswählen und Ergebnisse prüfen

Ein lokaler 200er beweist nicht, dass ein Anbieter in Frankfurt, mit Mobile-Exit oder mit Account-Login funktioniert. Er beweist, dass Ihr Client CONNECT, Auth und TLS so spricht, wie Requests 2.34.2 es erwartet.

Für den Alltag:

  1. Nehmen Sie den Endpunkt, den der Anbieter für HTTP-Clients dokumentiert, inklusive Schema und Port.
  2. Kodieren Sie Benutzer und Passwort, legen Sie sie in die Proxy-URL, nicht in HTTPProxyAuth, sobald HTTPS-Ziele vorkommen.
  3. Schalten Sie die Umgebung mit trust_env = False oder übergeben Sie proxies= an jedem Aufruf.
  4. Setzen Sie (connect, read) so knapp, dass ein toter Proxy Sie nicht blockiert.
  5. Vergleichen Sie den Origin, den Ihr Python-Request sieht, mit der Range, die der Anbieter für den Plan nennt. Meine IP im Browser ist ein zweiter Ausgang.

Residential- und Mobile-Exits unterscheiden sich in der Herkunft, nicht im proxies-Dict. Die Katalogseiten Residential-Proxys und Mobile Proxys filtern den Typ. Requests bleibt dieselbe Bibliothek. Wenn der Dienst hängt, grenzt Proxy funktioniert nicht die Schicht ein, bevor Sie den Client umschreiben.

SOAX: Residential- und Mobile-Proxys
SOAX: Residential- und Mobile-Proxys3.8218

SOAX verkauft rotierende Residential- und Mobile-Proxys über einen gemeinsamen Credit-Pool, mit Datacenter und ISP in denselben Plänen. Der Builder startet bei $200/mo + VAT (200 Credits); Tier-1-Traffic kostet $3/GB im Builder.

85 Länder
3,00 $
pro 1 GB
Website öffnen
Bright Data: Residential- und Datacenter-Proxys
Bright Data: Residential- und Datacenter-Proxys4.41449

Bright Data ist ein großes Proxy-Netz für Datenerhebung: Residential-, Mobile-, ISP- und Datacenter-IPs in rund 195 Ländern. Residential-Traffic lässt sich ohne Abo nach Verbrauch bezahlen, Datacenter- und ISP-Adressen gibt es in Paketen ab 10 IPs. Für Residential und Mobile ist eine Identitätsprüfung nötig.

10 Länder
5,00 $
pro 1 GB
Oxylabs — Residential- und Datacenter-Proxys
Oxylabs — Residential- und Datacenter-Proxys3.9773

Oxylabs verkauft Residential-, Mobile-, ISP- und Datacenter-Proxys in einem einzigen Monatsabo ab 30 $. Zahlung: Karte, Überweisung, AliPay, PayPal.

10 Länder
0,45 $
pro 1 GB
Decodo (Smartproxy): Residential- und Datacenter-Proxys
Decodo (Smartproxy): Residential- und Datacenter-Proxys4.22595

Decodo (ehemals Smartproxy) verkauft Residential- und Datacenter-Proxys. Residential: $4/GB ohne Monatsplan oder monatliche Pakete ab $3.75/GB für 3 GB. Zahlungen: Karten, PayPal, Alipay, Google/Apple Pay, Krypto (mit Einschränkungen).

83 Länder
0,38 $
pro 1 GB

Das ist keine Protokoll- oder Auth-Prüfung dieser Anbieter. Die Fixture hat sie nicht gerufen. Für Requests brauchen Sie einen HTTP-Forward-Proxy mit CONNECT für HTTPS-Ziele oder SOCKS5/SOCKS5h, Login in der URL, Benutzer ohne Doppelpunkt nach RFC 7617, Passwort komponentenweise kodiert. Ob ein Dienst das so anbietet, steht in dessen aktueller Doku.

Warum steht unter https oft eine http-Proxy-URL?

Requests schickt CONNECT über den HTTP-Proxy und baut TLS im Tunnel. Das Ziel ist https, der Proxy-Endpunkt bleibt http, außer der Anbieter terminiert TLS auf dem Proxy-Port.

Reicht HTTPProxyAuth für HTTPS-Seiten?

In Requests 2.34.2 mit urllib3 2.8.0 kam HTTP-GET damit durch. CONNECT zum HTTPS-Ziel endete mit ProxyError und 407. Legen Sie Benutzer und Passwort in die Proxy-URL, komponentenweise kodiert.

Warum überschreibt die Umgebung meine Session.proxies?

Die Docs beschreiben genau das. Ohne proxies= am Aufruf gewinnen HTTP_PROXY und HTTPS_PROXY in einem normalen Prozess. Unter CGI mit REQUEST_METHOD ignoriert CPython die Großschreibung von HTTP_PROXY. trust_env=False oder ein explizites Dict am get/post stoppt das.

Was bewirkt NO_PROXY, wenn ich proxies= setze?

NO_PROXY gilt für Proxys aus der Umgebung. In der Fixture ging 127.0.0.1 ohne Dict direkt. Mit proxies= am selben Host nutzte Requests den Proxy trotzdem.

Wann socks5, wann socks5h?

socks5 löst den Namen lokal. Ein unbekannter Host scheitert schon am Client, der Proxy sieht nichts. socks5h übergibt den Hostnamen. Das sah die Fixture an lab-origin.invalid.

Ist ProxyError immer ein 407?

Nein. Dieselbe Klasse kam beim lokalen Connect-Timeout zum Proxy und bei Connection refused. Lesen Sie die Kette. ConnectTimeoutError sitzt innen, oben steht ProxyError. Max retries exceeded heißt bei Default-Adapter total=0 nicht, dass mehrere Connects liefen.

Darf ich verify=False setzen, bis der Proxy läuft?

Nein als Normalfall. Das Laborziel scheiterte mit SSLError und lief mit der Test-CA. verify=False unterdrückt Hostname und Kette und gehört nicht in Produktionscode.