---
title: "Max Retries Exceeded With URL: Bedeutung und Lösung"
description: "„Max Retries Exceeded With URL“ ist in Requests die Hülle um den echten Verbindungsfehler. Die Ursache steht im Teil „Caused by“; so lesen und beheben Sie sie."
url: https://proxynet.io/de/blog/max-retries-exceeded-with-url
date: 2026-09-24
author: "Acar Diveroli"
category: "Anleitungen, Web Scraping"
lang: de
---

# Max Retries Exceeded With URL: Bedeutung und Lösung

Ihr Skript zur Preisbeobachtung lief die ganze Nacht ohne Probleme. Heute Morgen haben Sie die Proxy-Adresse aus Ihrem neuen Tarif eingefügt, und jetzt gibt das Terminal eine einzige lange Zeile aus: `HTTPSConnectionPool(host='example.com', port=443): Max retries exceeded with url`. Das Skript hat keine Einstellung für Wiederholungen, und der Host in der Meldung ist der Shop, den Sie auslesen, also richtet sich Ihr Blick sofort auf den Shop. Ganz am Ende der Zeile steht in den Klammern aber `Tunnel connection failed: 403 Forbidden`, und diese Antwort kam vom Proxy, nicht vom Shop.

Dieser Leitfaden nimmt die Meldung auseinander. Er erklärt, warum sie „max retries“ sagt, obwohl Sie keine Wiederholungen eingestellt haben, bietet eine Lesetabelle aus Fehlertexten, die wir selbst erzeugt haben, und behandelt die Proxy-Varianten, Timeouts und denselben Fehler in pip. Am Ende steht ein getestetes Python-Skript, das die Ursache benennt.

> **Hinweis: Kurzantwort**
>
> „Max retries exceeded with url“ ist der Text der urllib3-Ausnahme `MaxRetryError` und nennt für sich allein keine Ursache. Requests wiederholt standardmäßig nicht, deshalb sehen Sie die Meldung auch nach einem einzigen fehlgeschlagenen Versuch. Die Ursache steht im Teil `Caused by` in Klammern: `NameResolutionError` bedeutet, dass ein Name nicht aufgelöst wurde, `NewConnectionError`, dass die Verbindung abgelehnt wurde, `ConnectTimeoutError`, dass innerhalb des Timeouts keine Verbindung zustande kam, `SSLError`, dass das Zertifikat nicht geprüft werden konnte, und `ProxyError`, dass das Problem zwischen Ihnen und dem Proxy liegt. `Tunnel connection failed: 403` heißt, dass der Proxy Ihre CONNECT-Anfrage abgelehnt hat. Prüfen Sie, ob die Proxy-URL mit `http://` beginnt, prüfen Sie den Port, und übergeben Sie bei jeder Anfrage `timeout=(connect, read)`.

## Was bedeutet „Max retries exceeded with url“?

Die Meldung stammt von urllib3, der Bibliothek, die für Requests die Verbindungen öffnet. Gibt urllib3 eine Anfrage auf, wirft es `MaxRetryError`. Requests fängt diese Ausnahme ab und wirft eine eigene, die es nach der inneren Ursache auswählt: `ConnectTimeout`, `ProxyError`, `SSLError`, `RetryError`, wenn die Wiederholungen nach Statuscode aufgebraucht sind, oder schlicht `ConnectionError` für alles andere. Hier ein Fehlertext aus unserem Testlauf, in seine vier Teile zerlegt:

```text
requests.exceptions.ProxyError                        <- 1. die Requests-Ausnahme
HTTPSConnectionPool(host='example.com', port=443):    <- 2. der Pool: Ziel-Host und Port
Max retries exceeded with url: /                      <- 3. der Text der Hülle und der Pfad
(Caused by ProxyError('Unable to connect to proxy',   <- 4. die echte Ursache, die äußerste zuerst
    OSError('Tunnel connection failed: 403 Forbidden')))
```

An Teil 2 scheitern die meisten. Bei einer `https://`-URL zeigt die Pool-Zeile das **Ziel**, auch wenn die Anfrage über einen Proxy läuft; die Proxy-Adresse erscheint, wenn überhaupt, nur in Teil 4. Bei einer einfachen `http://`-URL über einen Proxy ist es umgekehrt: Die Pool-Zeile zeigt den Proxy, und Teil 3 enthält die vollständige Ziel-URL, etwa `HTTPConnectionPool(host='127.0.0.1', port=8083): Max retries exceeded with url: http://example.com/`.

## Warum steht dort „max retries“, obwohl Sie nie Wiederholungen eingestellt haben?

Requests wiederholt fehlgeschlagene Verbindungen standardmäßig nicht. Im Quelltext von `requests/adapters.py` steht `DEFAULT_RETRIES = 0`, und der Adapter macht daraus `Retry(0, read=False)`. Die urllib3-Dokumentation zur [Retry-Klasse](https://urllib3.readthedocs.io/en/stable/reference/urllib3.util.html) hält fest, dass Fehler in `MaxRetryError` verpackt werden, sofern Wiederholungen nicht mit `retries=False` abgeschaltet sind. Null Wiederholungen gelten nicht als abgeschaltet, deshalb erscheint schon ein einziger fehlgeschlagener Versuch als „Max retries exceeded“.

Mehr Wiederholungen beheben die Ursache nicht. Ein falsch geschriebener Proxy-Name, ein falscher Port oder ein abgelehnter Tunnel scheitert jedes Mal auf dieselbe Weise. In unserem Test scheiterte eine nicht erreichbare Adresse mit 2 Sekunden Verbindungs-Timeout in der Standardeinstellung nach 2 Sekunden, mit zwei Wiederholungen und kurzem Backoff nach 7 Sekunden. Wiederholungen helfen nur bei kurzen Netzaussetzern.

## Wie lesen Sie die Fehlermeldung Schritt für Schritt?

Lesen Sie die Meldung von außen nach innen:

1. **Suchen Sie die Requests-Ausnahme.** `ProxyError` bedeutet, dass der Fehler auf dem Weg zum Proxy oder beim Proxy selbst auftrat.
2. **Lesen Sie die Pool-Zeile.** Bei `https://`-Zielen sind `host` und `port` das Ziel. Port 443 über einen HTTP-Proxy bedeutet, dass die Anfrage durch einen CONNECT-Tunnel läuft.
3. **Lesen Sie die erste Klasse nach `Caused by`.** Das ist die Diagnose von urllib3: `NameResolutionError`, `NewConnectionError`, `ConnectTimeoutError`, `SSLError` oder `ProxyError`.
4. **Lesen Sie den innersten Text.** `Failed to resolve 'name'`, `[Errno 111] Connection refused` unter Linux, `[WinError 10061]` unter Windows, `CERTIFICATE_VERIFY_FAILED` oder `Tunnel connection failed: 403 Forbidden`. Ein `host=` an dieser Stelle kann der Proxy sein.
5. **Achten Sie darauf, wie lange der Aufruf gedauert hat.** Ein Fehler nach genau Ihrem Verbindungs-Timeout (oder einem Vielfachen davon) ist ein Timeout; ein schnellerer ist eine Ablehnung oder ein zurückgewiesener Tunnel. Unter Windows dauerte eine abgelehnte Verbindung in unseren Tests etwa zwei Sekunden pro Adresse, und `localhost` vier (erst IPv6, dann IPv4).

## HTTPSConnectionPool-Fehler: Was der Teil „Caused by“ verrät

Alle folgenden Fehlertexte haben wir unter Python 3.13 mit Requests 2.34.2 und urllib3 2.8.0 über einen lokalen Test-Proxy erzeugt. Lange Teile sind mit `...` gekürzt; der Windows-Fehlertext hängt außerdem von der Systemsprache ab.

| Caused by (innerster Teil) | Requests-Ausnahme | Was passiert ist | Zuerst prüfen |
|---|---|---|---|
| `NameResolutionError(... Failed to resolve 'no-such-host.invalid' ...)` | `ConnectionError` | Der Zielname wurde nicht aufgelöst | Schreibweise der URL, DNS, VPN |
| `NewConnectionError(... [Errno 111] or [WinError 10061] ...)` | `ConnectionError` | Auf diesem Port lauscht nichts | Läuft der Dienst, stimmt der Port |
| `ConnectTimeoutError(... 'Connection to 10.255.255.1 timed out. (connect timeout=2)')` | `ConnectTimeout` | Keine TCP-Verbindung innerhalb des Timeouts | Adresse, Firewall, Timeout-Wert |
| `SSLError(SSLCertVerificationError(... CERTIFICATE_VERIFY_FAILED ...))` | `SSLError` | Dem Zertifikat wird nicht vertraut | certifi, Stammzertifikat der Firma |
| `ProxyError('Unable to connect to proxy', NewConnectionError(...))` | `ProxyError` | Der Proxy-Port hat die Verbindung abgelehnt | Proxy-Host und -Port, lokale Firewall |
| `ProxyError('Unable to connect to proxy', NameResolutionError(... 'pr.proxynet.invalid' ...))` | `ProxyError` | Der Proxy-Name wurde nicht aufgelöst | Tippfehler im Proxy-Host |
| `ProxyError('Unable to connect to proxy', ConnectTimeoutError(...))` | `ProxyError` | Der Proxy hat nicht rechtzeitig geantwortet | Proxy-Adresse, ausgehende Regeln für diesen Port |
| `ProxyError(..., OSError('Tunnel connection failed: 403 Forbidden'))` | `ProxyError` | Der Proxy hat den CONNECT-Tunnel abgelehnt | Zielport, Proxy-Typ und -Port, Freigaberegeln |
| `ProxyError(..., OSError('Tunnel connection failed: 407 Proxy Authentication Required'))` | `ProxyError` | Der Proxy verlangt gültige Zugangsdaten | Siehe [Proxy-Authentifizierung](/de/blog/proxy-authentication-methods) |
| `ProxyError(..., OSError('Tunnel connection failed: 502 Bad Gateway'))` | `ProxyError` | Der Proxy hat das Ziel nicht erreicht | Ziel-Host und -Port |
| `ProxyError('... Your proxy appears to only use HTTP and not HTTPS ...', SSLError(... WRONG_VERSION_NUMBER ...))` | `ProxyError` | Die Proxy-URL beginnt mit `https://` | `http://` schreiben |

Drei Details stehen nicht in der Tabelle. Ein Proxy, der in ein Timeout läuft, wirft `ProxyError`, `except ConnectTimeout` bekommt ihn also nie zu sehen. Ein Lese-Timeout wird nicht verpackt: Es kommt als `ReadTimeout` mit dem Text `Read timed out. (read timeout=3)`. Und eine einfache `http://`-URL über einen Proxy mit falschem Passwort wirft gar nichts; Sie erhalten eine Antwort mit dem Status `407`.

## Was bedeutet „ProxyError: Cannot connect to proxy“?

`Cannot connect to proxy.` (urllib3 1.26) und `Unable to connect to proxy` (urllib3 2.x) sind derselbe Fehler. Beide können auf einem Rechner vorkommen: pip 25.x bringt urllib3 1.26.20 mit, während eine frische Requests-Installation urllib3 2.8.0 nachzieht. Lesen Sie das zweite Argument, nicht den Wortlaut:

- **`NewConnectionError`**: Auf diesem Proxy-Port lauscht nichts, oder eine lokale Firewall blockiert ihn. Kopieren Sie Host und Port erneut aus dem Panel.
- **`NameResolutionError`**: Der Host-Name des Proxys ist falsch geschrieben, oder Ihr DNS kann ihn nicht auflösen.
- **`ConnectionResetError`** (`Errno 104` unter Linux, `WinError 10054` unter Windows): Der Proxy oder ein Gerät auf dem Weg hat die Verbindung geschlossen.
- **`OSError('Tunnel connection failed: ...')`**: Der Proxy hat geantwortet, aber den Tunnel nicht geöffnet; der Statuscode ist seine Antwort.

Die Browser-Variante, „Proxyserver reagiert nicht“, behandelt [Proxy-Fehler: Was tun, wenn der Proxyserver nicht reagiert?](/de/blog/proxy-server-not-responding)

## Tunnel connection failed: 403 Forbidden: Warum lehnt der Proxy CONNECT ab?

Eine `https://`-Anfrage über einen HTTP-Proxy beginnt mit `CONNECT example.com:443`. Der Proxy öffnet eine TCP-Verbindung zum Ziel und reicht verschlüsselte Bytes durch; die Seite selbst sieht er nie. Nach [RFC 9110, Abschnitt 9.3.6](https://www.rfc-editor.org/rfc/rfc9110.html#name-connect) bedeutet jede Antwort außer 2xx, dass kein Tunnel zustande kam. Das Python-Modul `http.client` liest diese Antwort und schreibt `Tunnel connection failed`.

Das `403` ist die Entscheidung des Proxys; das Ziel hat Ihre Anfrage nie gesehen. Der RFC empfiehlt, dass Proxys CONNECT auf bekannte Ports oder eine Liste sicherer Ziele beschränken, weil ein Tunnel zu Port 25 Spam weiterleiten könnte. Häufige Gründe:

- **Der Zielport ist nicht erlaubt.** `https://example.com:8443/` kann scheitern, während `https://example.com/` funktioniert.
- **Eine Freigabeliste.** Firmen-Proxys und manche Hosting-Plattformen erlauben nur freigegebene Domains. Fragen Sie das IT-Team oder lesen Sie die Dokumentation der Plattform; umgehen Sie die Richtlinie nicht.
- **Falscher Proxy-Typ oder falscher Port**, zum Beispiel ein Port, der für ein anderes Protokoll gedacht ist.
- **Regeln des Anbieters.** Ein Anbieter kann Tunnel zu bestimmten Zielen ablehnen; seine Dokumentation nennt die Statuscodes, die er dafür nutzt.

Ein `403` der Zielseite kommt als normale Antwort mit einer Seite an; diesen Fall behandelt [HTTP-Statuscodes beim Web Scraping](/de/blog/http-status-codes-web-scraping). Ein `407` beim Tunnel bedeutet, dass der Proxy Zugangsdaten verlangt.

## Soll die Proxy-URL mit http:// oder https:// beginnen?

Im Dictionary `proxies` ist der Schlüssel das Schema des **Ziels** und der Wert der Weg zum **Proxy**. Das Beispiel in der Requests-Dokumentation ordnet `'https'` den Wert `'http://10.10.1.10:1080'` zu. Die meisten Proxys sprechen einfaches HTTP und öffnen für verschlüsselte Websites einen CONNECT-Tunnel (siehe unsere Seite [HTTPS-Proxy](/de/https-proxy)), daher bekommen beide Schlüssel einen Wert mit `http://`:

```python
proxies = {
    "http": "http://user:pass@pr.proxynet.io:8000",
    "https": "http://user:pass@pr.proxynet.io:8000",  # auch hier http://
}
```

Steht `https://` im Wert, öffnet urllib3 2.x eine TLS-Verbindung zum Proxy selbst, ein einfacher HTTP-Proxy antwortet mit Bytes ohne TLS, und Sie erhalten `WRONG_VERSION_NUMBER` mit dem Hinweis `Your proxy appears to only use HTTP and not HTTPS`. Die urllib3-Seite zu [diesem Fehler](https://urllib3.readthedocs.io/en/latest/advanced-usage.html#https-proxy-error-http-proxy) nennt dieselbe Lösung, auch für eine falsch gesetzte Variable `HTTPS_PROXY`.

Zwei verwandte Punkte:

- **SOCKS.** Installieren Sie `requests[socks]`. Mit `socks5://` löst Ihr Rechner den Namen auf; in unserem Test scheiterte ein falscher Name deshalb, bevor der Proxy überhaupt kontaktiert wurde. Mit `socks5h://` löst der Proxy ihn auf ([SOCKS und HTTP Proxy im Vergleich](/de/blog/socks-vs-http-proxy), [SOCKS5-Proxy](/de/socks5-proxy)).
- **Umgebungsvariablen.** Die [Requests-Seite zur fortgeschrittenen Nutzung](https://requests.readthedocs.io/en/latest/user/advanced/) warnt, dass Proxys aus der Umgebung `session.proxies` überschreiben können, und empfiehlt `proxies=` bei jeder Anfrage. Die Variablen erklärt [Proxy-Nutzung mit wget](/de/blog/wget-proxy).

## Was passiert ohne Timeout? ConnectTimeout und ReadTimeout

Requests hat kein Standard-Timeout: Ohne `timeout=` kann ein Aufruf minutenlang hängen und liefert Ihnen keinen Fehler, den Sie lesen könnten. Übergeben Sie ein Tupel wie `timeout=(3.05, 20)`: erst das Verbindungs-Timeout, dann das Lese-Timeout, in Sekunden. Die Dokumentation empfiehlt ein Verbindungs-Timeout knapp über einem Vielfachen von 3, dem Standardfenster für TCP-Neuübertragungen.

Die beiden Timeouts scheitern unterschiedlich:

- **ConnectTimeout**: keine TCP-Verbindung in der vorgegebenen Zeit. Es kommt verpackt in „Max retries exceeded“, und die API-Referenz bezeichnet es als sicher wiederholbar.
- **ReadTimeout**: verbunden, aber innerhalb des Lese-Timeouts keine Daten. Es wird nicht verpackt, und der Server hat die Anfrage womöglich schon verarbeitet; ein wiederholter POST kann die Arbeit also doppelt ausführen.

Das Verbindungs-Timeout gilt pro IP-Adresse, ein Name mit IPv4- und IPv6-Adressen kann die Wartezeit also verdoppeln. Das Lese-Timeout ist die Pause zwischen zwei Bytes, keine Grenze für den ganzen Download. Die Standardwerte anderer Bibliotheken stehen in [HTTPX, Requests und AIOHTTP im Vergleich](/de/blog/httpx-vs-requests-vs-aiohttp).

## Derselbe Fehler in pip: „Retrying ... after connection broken by“

pip bringt eigene Kopien von Requests und urllib3 mit, deshalb scheitert `pip install` aus denselben Gründen. Die [pip-Dokumentation](https://pip.pypa.io/en/stable/cli/pip/) nennt die Standardwerte: `--retries 5` und `--timeout 15` Sekunden. Wir haben pip 26.2.1 mit `--retries 2` über einen lokalen Proxy laufen lassen, der jedes CONNECT mit `403` ablehnt:

```text
WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'OSError('Tunnel connection failed: 403 Forbidden')': /simple/six/
WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'OSError('Tunnel connection failed: 403 Forbidden')': /simple/six/
ERROR: Could not find a version that satisfies the requirement six (from versions: none)
ERROR: No matching distribution found for six
```

Das Paket existiert; pip hat den Index nie erreicht. Die Ursache steht in den `WARNING`-Zeilen. pip 25.2 gab dieselbe Warnung mit dem Wortlaut von urllib3 1.26 aus: `ProxyError('Cannot connect to proxy.', ...)`.

Bei `CERTIFICATE_VERIFY_FAILED` hinter einem Firmen-Proxy, der TLS inspiziert, übergeben Sie das Stammzertifikat der Firma mit `--cert`; `--trusted-host` schaltet die Prüfung ab und ist nur der letzte Ausweg. Wie Sie den Proxy für pip einstellen, steht in [Linux-Proxy-Einstellungen](/de/blog/linux-proxy-settings).

## Ein Python-Skript, das die Ursache benennt

Das Skript macht aus einer Ausnahme eine einzige Zeile: was fehlgeschlagen ist und was Sie prüfen sollten. Es

- wiederholt nur fehlgeschlagene Verbindungen, und zwar zweimal;
- behält `read=False`: In unserem Test machte `read=0` aus einem `ReadTimeout` einen verpackten `ConnectionError`;
- setzt `other=0`: Ohne diese Einstellung wurde ein mit `403` oder `407` abgelehnter Tunnel dreimal versucht;
- übergibt bei jeder Anfrage `proxies=` und `timeout=(3.05, 20)`;
- fängt `ProxyError`, `SSLError` und `ConnectTimeout` vor ihrer Elternklasse `ConnectionError` ab.

Wiederholungen nach Statuscode sind eine eigene Ebene ([HTTP-Statuscodes beim Web Scraping](/de/blog/http-status-codes-web-scraping)), ebenso die Rotation ([Proxys in Python rotieren](/de/blog/how-to-rotate-proxies-in-python)).

```python
"""Findet die echte Ursache hinter "Max retries exceeded with url" und sagt, was zu prüfen ist."""
import re
import ssl
import time

import requests
from requests.adapters import HTTPAdapter
from urllib3.exceptions import (
    ConnectTimeoutError,
    NameResolutionError,
    NewConnectionError,
    ProxyError as Urllib3ProxyError,
)
from urllib3.util import Retry

PROXY = "http://user:pass@pr.proxynet.io:8000"  # http:// auch für https://-Ziele
TIMEOUT = (3.05, 20)  # Sekunden: (Verbindung, Lesen)

def make_session():
    """Eine Session, die fehlgeschlagene Verbindungen zweimal wiederholt und sonst nichts."""
    retry = Retry(
        total=2,
        connect=2,      # DNS-Fehler, abgelehnte Verbindungen und Verbindungs-Timeouts
        read=False,     # ReadTimeout bleibt ReadTimeout: Der Server hat die Anfrage vielleicht schon
        other=0,        # ein Proxy, der den Tunnel abgelehnt hat, lehnt ihn wieder ab
        status=0,       # Wiederholungen nach Statuscode gehören in eine andere Ebene
        backoff_factor=0.5,
    )
    adapter = HTTPAdapter(max_retries=retry)
    session = requests.Session()
    session.mount("http://", adapter)
    session.mount("https://", adapter)
    return session

def causes(exc):
    """Die Ausnahme und jeder darin verpackte Fehler, der äußerste zuerst."""
    chain = []
    while exc is not None and all(exc is not seen for seen in chain):
        chain.append(exc)
        inner = None
        for candidate in (getattr(exc, "reason", None), getattr(exc, "original_error", None),
                          exc.__cause__, exc.__context__, *exc.args[:2]):
            if isinstance(candidate, BaseException):
                inner = candidate
                break
        exc = inner
    return chain

def explain(exc):
    """Eine Zeile: was fehlgeschlagen ist, auf welcher Seite, und was zuerst zu prüfen ist."""
    chain = causes(exc)
    text = " | ".join(str(e) for e in chain)
    side = "proxy" if any(isinstance(e, Urllib3ProxyError) for e in chain) else "target"

    tunnel = re.search(r"Tunnel connection failed: (\d{3})", text)
    if tunnel:
        code = tunnel.group(1)
        if code == "407":
            return "proxy asked for credentials (407): check user:pass or the IP whitelist"
        if code == "403":
            return "proxy refused the CONNECT tunnel (403): check the target port and the proxy's allow rules"
        return f"proxy was reached but could not reach the target ({code}): check the target host and port"
    if "appears to only use HTTP" in text:
        return "proxy URL starts with https:// but the proxy speaks plain HTTP: write http://"
    if any(isinstance(e, NameResolutionError) for e in chain):
        host = re.search(r"Failed to resolve '([^']+)'", text)
        return f"{side} name {host.group(1) if host else ''} does not resolve: check spelling, DNS and VPN"
    if any(isinstance(e, ssl.SSLCertVerificationError) for e in chain):
        return "certificate not trusted: update certifi or set REQUESTS_CA_BUNDLE to your company root CA"
    if isinstance(exc, requests.exceptions.ReadTimeout):
        return "connected, but no answer within the read timeout: the server may have the request"
    if any(isinstance(e, NewConnectionError) for e in chain):
        return f"{side} refused the connection: wrong port, service down, or a firewall rejects it"
    if any(isinstance(e, ConnectTimeoutError) for e in chain):
        return f"no TCP connection to the {side} within the connect timeout: check address, port, firewall"
    return f"unrecognised, read the innermost error: {chain[-1]!r}"

def fetch(session, url, proxy=PROXY):
    """Ruft eine URL per GET ab und gibt den Status aus, oder die Diagnose, wenn die Anfrage scheitert."""
    proxies = {"http": proxy, "https": proxy} if proxy else None  # pro Anfrage: Umgebungsvariablen überschreiben es nicht
    start = time.monotonic()
    try:
        resp = session.get(url, proxies=proxies, timeout=TIMEOUT)
    except requests.exceptions.ProxyError as exc:      # vor ConnectionError: Es ist eine Unterklasse
        kind, error = "ProxyError", exc
    except requests.exceptions.SSLError as exc:        # ebenfalls ein ConnectionError
        kind, error = "SSLError", exc
    except requests.exceptions.ConnectTimeout as exc:  # ConnectionError und Timeout zugleich
        kind, error = "ConnectTimeout", exc
    except requests.exceptions.ReadTimeout as exc:     # nie in "Max retries exceeded" verpackt
        kind, error = "ReadTimeout", exc
    except requests.exceptions.ConnectionError as exc:
        kind, error = "ConnectionError", exc
    else:
        print(f"{'OK':<16}{time.monotonic() - start:6.2f}s  {url}  HTTP {resp.status_code}")
        return resp
    print(f"{kind:<16}{time.monotonic() - start:6.2f}s  {url}\n{'':<24}{explain(error)}")
    return None

if __name__ == "__main__":
    session = make_session()
    for url in ["https://httpbin.org/ip", "https://example.com/"]:
        fetch(session, url)
```

In urllib3 2.x ist `NameResolutionError` eine Unterklasse von `NewConnectionError`, und diese wiederum eine Unterklasse von `ConnectTimeoutError`; deshalb prüft `explain()` die speziellste Klasse zuerst. Das Skript braucht nur `pip install requests`.

### So sieht die Ausgabe aus

Wir haben `fetch()` unter Windows 11 für jeden Fall einmal ausgeführt: mit einem lokalen Test-Proxy (richtiges und falsches Passwort, geschlossener Port, falsch geschriebener Name, Schema `https://`), mit einem zweiten, der jedes CONNECT ablehnt, und mit echten Hosts für die Zertifikats- und Timeout-Fälle. Das Lese-Timeout lag bei 5 Sekunden:

```text
OK                0.77s  https://httpbin.org/ip  HTTP 200
ProxyError        0.02s  https://httpbin.org/ip
                        proxy asked for credentials (407): check user:pass or the IP whitelist
ProxyError        0.00s  https://example.com/
                        proxy refused the CONNECT tunnel (403): check the target port and the proxy's allow rules
ProxyError        0.02s  https://no-such-host.invalid/
                        proxy was reached but could not reach the target (502): check the target host and port
ProxyError        7.10s  https://httpbin.org/ip
                        proxy refused the connection: wrong port, service down, or a firewall rejects it
ProxyError        1.02s  https://httpbin.org/ip
                        proxy name pr.proxynet.invalid does not resolve: check spelling, DNS and VPN
ProxyError        0.21s  https://httpbin.org/ip
                        proxy URL starts with https:// but the proxy speaks plain HTTP: write http://
SSLError          0.63s  https://self-signed.badssl.com/
                        certificate not trusted: update certifi or set REQUESTS_CA_BUNDLE to your company root CA
ConnectTimeout   10.16s  http://10.255.255.1/
                        no TCP connection to the target within the connect timeout: check address, port, firewall
ReadTimeout       5.02s  http://127.0.0.1:8082/
                        connected, but no answer within the read timeout: the server may have the request
ConnectionError  13.12s  http://localhost:8083/
                        target refused the connection: wrong port, service down, or a firewall rejects it
```

Die abgelehnten Tunnel scheiterten in Millisekunden, weil `other=0` sie stoppte. Der geschlossene Proxy-Port brauchte drei Versuche von je etwa zwei Sekunden plus eine Sekunde Backoff, beim Verbindungs-Timeout waren es dreimal 3,05 Sekunden plus Backoff, bei `localhost` 13 Sekunden, weil jeder Versuch erst IPv6 und dann IPv4 probierte. Das `502` stammte von unserem Test-Proxy, der das Ziel nicht auflösen konnte.

## Wo Ihnen dieser Fehler begegnet

- **Scraping über einen Proxy:** Proxy-Fehler zeigen sich hier noch vor jedem Statuscode ([Data Scraping](/de/data-scraping)).
- **CI- und Docker-Builds hinter einem Firmen-Proxy:** Builds übernehmen die Proxy-Einstellungen des Hosts oft nicht, und `localhost` ist dort der Container ([Linux-Proxy-Einstellungen](/de/blog/linux-proxy-settings)).
- **API-Integrationen mit fester Ausgangs-IP:** Eine Tunnel-Ablehnung sieht aus wie ein Ausfall der API ([Statische IP für APIs](/de/blog/static-ip-for-api-access)).
- **Test eines neuen Proxys:** Senden Sie eine einzelne Anfrage mit Timeout, bevor Sie einen ganzen Job starten ([So testen Sie einen Proxy](/de/blog/how-to-test-a-proxy)).
- **Browserautomatisierung:** Derselbe Tunnelfehler erscheint als `ERR_TUNNEL_CONNECTION_FAILED` ([Playwright mit Proxy](/de/blog/playwright-proxy)).
- **Firmennetze:** Eine Firewall und ein Proxy filtern beide Verbindungen ([Proxy und Firewall](/de/blog/proxy-vs-firewall)).

## Häufige Fehler

- **Die Zahl der Wiederholungen erhöhen.** Eine dauerhafte Ursache bleibt dauerhaft; Sie warten nur länger.
- **Dem Ziel die Schuld geben, weil sein Name in der Pool-Zeile steht.** Bei `https://`-URLs erscheint der Proxy nur in den Klammern.
- **Eine alte Variable `HTTPS_PROXY` vergessen.** Sie kann `session.proxies` stillschweigend ersetzen.
- **`verify=False` in der Produktion stehen lassen.** Die Requests-Dokumentation warnt, dass Sie sich damit Man-in-the-Middle-Angriffen aussetzen. Aktualisieren Sie certifi oder setzen Sie `REQUESTS_CA_BUNDLE`; für einen lokalen Debugging-Proxy siehe [MITM-Proxy](/de/blog/mitm-proxy).
- **`ConnectionError` zuerst abfangen.** Es verschluckt `ProxyError`, `SSLError` und `ConnectTimeout`.
- **Tunnel-`403` und `407` verwechseln.** Das eine ist eine Regel, das andere eine Frage der Zugangsdaten.
- **Die letzte Zeile von pip lesen.** Die Ursache steht in den `WARNING`-Zeilen darüber.

## Entscheidungshilfe

| Was Sie sehen | Was zu tun ist |
|---|---|
| `NameResolutionError` in Caused by | Den fehlerhaften Namen korrigieren (Proxy-Host oder URL); keine Wiederholungen |
| `ProxyError` mit `NewConnectionError` | Proxy-Host und -Port neu kopieren; lokale Firewall prüfen |
| `Tunnel connection failed: 403` | Zielport, Proxy-Typ und -Port, Freigaberegeln prüfen |
| `Tunnel connection failed: 407` | Zugangsdaten oder IP-Whitelist prüfen |
| `SSLError: CERTIFICATE_VERIFY_FAILED` | certifi aktualisieren oder `REQUESTS_CA_BUNDLE` setzen (pip: `--cert`) |
| Aufrufe hängen oder laufen ab und zu in ein Timeout | `timeout=(3.05, 20)` plus ein `Retry` nur für Verbindungen |
| pip meldet „No matching distribution found“ | Die Zeile `WARNING: Retrying` lesen |

## Häufige Fragen

### Behebt eine höhere Zahl an Wiederholungen „Max retries exceeded“?

Nur wenn das Netz kurz aussetzt. Bei einem Namen, der sich nicht auflösen lässt, einem geschlossenen Port oder einem abgelehnten Tunnel scheitert jede Wiederholung auf dieselbe Weise. Lesen Sie zuerst den Teil `Caused by`.

### Welches Standard-Timeout hat Python Requests?

Keines: Ohne `timeout=` kann eine Anfrage unbegrenzt warten. Geben Sie jedem Aufruf ein Tupel `(connect, read)` mit.

### Wird das Timeout in Requests in Sekunden oder Millisekunden angegeben?

In Sekunden. Eine Gleitkommazahl wie `3.05` funktioniert; eine einzelne Zahl setzt beide Phasen, ein Tupel wie `(3.05, 20)` setzt sie getrennt.

### Kann ich bei CERTIFICATE_VERIFY_FAILED verify=False verwenden?

Nur für einen schnellen lokalen Test, denn damit kann jeder auf dem Weg den Verkehr mitlesen oder verändern. Die dauerhafte Lösung ist ein aktuelles certifi oder, hinter einem Firmen-Proxy, der TLS inspiziert, dessen Stammzertifikat in `REQUESTS_CA_BUNDLE`.

### Warum meldet pip „No matching distribution found“, obwohl das Paket existiert?

pip konnte den Paketindex nicht erreichen und hat deshalb keine Versionen gefunden. Die Zeilen `WARNING: Retrying ... after connection broken by` darüber nennen die echte Ursache, etwa `Tunnel connection failed: 403 Forbidden`.

### Warum erhalte ich diesen Fehler beim Aufruf von localhost:8000?

Auf diesem Port lauscht nichts: Der Server läuft nicht, nutzt einen anderen Port, oder Ihr Code läuft in Docker, wo `localhost` der Container ist. Der innerste Fehler lautet `[Errno 111]` oder `[WinError 10061]`. Wie Sie herausfinden, welches Programm einen Port belegt, erklärt [Was ist Port 8080?](/de/blog/port-8080)

## Fazit

„Max retries exceeded with url“ ist eine Hülle: Die Ursache steht nach `Caused by`, und die Meldung erscheint schon nach einem Versuch, weil Requests standardmäßig nicht wiederholt. Unterscheiden Sie bei Proxy-Fehlern einen Fehler auf dem Weg zum Proxy von einem Tunnel, den der Proxy abgelehnt hat, und behalten Sie `http://` in der Proxy-URL. Geben Sie jeder Anfrage ein Timeout-Tupel mit und wiederholen Sie nur Verbindungsfehler. Testen Sie eine neue Einrichtung zuerst mit einer einzelnen Anfrage ([so testen Sie einen Proxy](/de/blog/how-to-test-a-proxy)), und vergleichen Sie die Optionen auf unserer Seite zu [Proxy-Diensten](/de/proxy).
