← Torna al blog

Python requests: ProxyError, SSLError WRONG_VERSION_NUMBER e 407 - come risolvere nella pratica

Analisi dettagliata degli errori comuni nell'uso dei proxy con Python requests: ProxyError, SSLError WRONG_VERSION_NUMBER, 407 Proxy Authentication Required. Esempi di codice funzionanti per ogni situazione.

📅11 novembre 2026

Se stai estraendo dati da siti web o automatizzando richieste tramite proxy in Python, prima o poi ti imbatterai in tre errori classici: ProxyError, SSLError: WRONG_VERSION_NUMBER e HTTP 407 Proxy Authentication Required. In questo articolo analizzeremo la causa di ciascuno di essi e mostreremo codice funzionante per la risoluzione.

Come requests lavora con i proxy: sintassi di base

La libreria requests accetta i proxy tramite un dizionario proxies, dove le chiavi sono lo schema della richiesta (http e https), e i valori sono gli URL del server proxy con autorizzazione opzionale.

import requests

proxies = {
    "http": "http://user:password@proxy_host:port",
    "https": "http://user:password@proxy_host:port",
}

response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.json())

Nota: anche per le richieste HTTPS, lo schema del proxy è solitamente specificato come http://, perché la maggior parte dei server proxy accetta un tunnel CONNECT tramite il protocollo HTTP, e non lo incapsula in SSL. Se specifichi https:// per un proxy che non lo supporta, riceverai un SSLError, la cui analisi sarà fornita più avanti.

Se stai lavorando con proxy SOCKS5, sarà necessario un pacchetto aggiuntivo:

pip install requests[socks]

# E la sintassi dei proxy cambia:
proxies = {
    "http": "socks5://user:password@proxy_host:port",
    "https": "socks5://user:password@proxy_host:port",
}

ProxyError: cause e soluzioni

requests.exceptions.ProxyError è un wrapper attorno a eccezioni di rete a basso livello che si verificano quando si tenta di stabilire una connessione con un server proxy. Le situazioni più comuni sono quattro.

1. Proxy non disponibile o indirizzo/porta errati

requests.exceptions.ProxyError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: / (Caused by ProxyError('Cannot connect to proxy.',
ConnectionResetError(104, 'Connection reset by peer')))

Controlla che l'host e la porta siano inseriti correttamente e che il server proxy sia effettivamente attivo. Il modo più veloce per diagnosticare è provare a connettersi tramite curl:

curl -x http://user:password@proxy_host:port https://httpbin.org/ip -v

2. Il proxy ha bloccato la tua richiesta (ban per IP o User-Agent)

I proxy dei data center vengono spesso bloccati dai siti target perché i loro range di IP sono noti ai sistemi antifrode. Se l'errore si verifica costantemente quando si accede a un sito specifico (ad esempio, Wildberries, Ozon o social network), e non a tutti i siti contemporaneamente, il problema non è nel proxy stesso, ma nella sua reputazione. In questo caso, passare a proxy residenziali può aiutare — utilizzano IP di utenti reali e vengono bloccati meno frequentemente dai sistemi antifrode.

3. Limite di connessioni simultanee superato

Se stai eseguendo un'estrazione multithreading e apri centinaia di connessioni tramite lo stesso proxy, il server potrebbe iniziare a chiudere le connessioni in eccesso. La soluzione è utilizzare un pool di connessioni tramite requests.adapters.HTTPAdapter con un limite pool_maxsize e max_retries:

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
retries = Retry(total=3, backoff_factor=1, status_forcelist=[502, 503, 504])
adapter = HTTPAdapter(max_retries=retries, pool_connections=20, pool_maxsize=20)

session.mount("http://", adapter)
session.mount("https://", adapter)

response = session.get("https://httpbin.org/ip", proxies=proxies, timeout=10)

4. Timeout dal lato del server proxy

Se il proxy "rimane bloccato" oltre il timeout impostato, vedrai ProxyError, avvolto in ReadTimeoutError. Aumenta il timeout a un valore ragionevole (di solito 15-30 secondi sono sufficienti) e aggiungi la gestione delle eccezioni separatamente:

try:
    response = requests.get(url, proxies=proxies, timeout=20)
except requests.exceptions.ProxyError as e:
    print(f"Problema con il proxy: {e}")
except requests.exceptions.Timeout:
    print("Il proxy non ha risposto nel tempo assegnato")

SSLError WRONG_VERSION_NUMBER: analisi del problema

Questo errore è uno dei più incomprensibili a prima vista, ma in realtà ha quasi sempre una sola causa.

requests.exceptions.SSLError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL:
WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123)')))

WRONG_VERSION_NUMBER significa che il client ha tentato di eseguire un handshake SSL/TLS dove il server si aspetta traffico HTTP normale (senza crittografia). La causa più comune è un errore nello schema del proxy specificato nel dizionario proxies.

Errore comune: specificare https:// invece di http:// nell'indirizzo del server proxy. La maggior parte dei proxy HTTP(S) accetta connessioni tramite HTTP normale, anche quando proxyizzano il traffico HTTPS tramite il meccanismo CONNECT.

# ERRATO — causerà WRONG_VERSION_NUMBER
proxies = {
    "http": "https://user:password@proxy_host:port",
    "https": "https://user:password@proxy_host:port",
}

# CORRETTO
proxies = {
    "http": "http://user:password@proxy_host:port",
    "https": "http://user:password@proxy_host:port",
}

Un'altra causa comune è una porta che fisicamente non supporta TLS. Ad esempio, se il fornitore di proxy ha assegnato la porta 8080 solo per il traffico HTTP, e stai tentando di stabilire una connessione TLS direttamente (saltando CONNECT), il server risponderà con testo normale invece di un handshake TLS, e la libreria client OpenSSL genererà proprio questo errore. Controlla nella documentazione del tuo fornitore di proxy quali porte e protocolli (HTTP/HTTPS/SOCKS5) sono disponibili su ciascuna porta specifica.

Un terzo scenario è che stai utilizzando socket o librerie di terze parti per stabilire una connessione TCP diretta con il proxy, bypassando il meccanismo standard di requests. In questo caso, assicurati che la crittografia TLS venga applicata solo al sito web di destinazione reale, e non alla connessione stessa con il proxy.

407 Proxy Authentication Required: autorizzazione

Il codice di risposta 407 è l'equivalente dello standard 401, ma per i server proxy. Significa che il server richiede l'autenticazione, e tu o non l'hai fornita, o l'hai fornita in un formato errato.

Metodo 1: login e password direttamente nell'URL

proxies = {
    "http": "http://login:password@proxy_host:port",
    "https": "http://login:password@proxy_host:port",
}

response = requests.get("https://httpbin.org/ip", proxies=proxies)

Questo metodo è il più semplice, ma se il login o la password contengono caratteri speciali (ad esempio, @, :, /), devono essere eseguite l'escape tramite urllib.parse.quote:

from urllib.parse import quote

login = quote("user@domain")
password = quote("p@ssw:ord!")

proxy_url = f"http://{login}:{password}@proxy_host:port"
proxies = {"http": proxy_url, "https": proxy_url}

Metodo 2: intestazione Proxy-Authorization manualmente

Se il fornitore di proxy utilizza uno schema di autorizzazione non standard o hai bisogno di maggiore controllo, puoi costruire l'intestazione manualmente tramite codifica base64:

import base64
import requests

login = "user"
password = "password"
credentials = base64.b64encode(f"{login}:{password}".encode()).decode()

headers = {
    "Proxy-Authorization": f"Basic {credentials}"
}

proxies = {
    "http": "http://proxy_host:port",
    "https": "http://proxy_host:port",
}

response = requests.get(
    "https://httpbin.org/ip",
    proxies=proxies,
    headers=headers,
    timeout=10
)

Importante: alcuni fornitori di proxy legano l'autenticazione non a login/password, ma a un IP statico specifico del client (whitelist). Se cambi IP (ad esempio, lavori tramite VPN o IP dinamico del fornitore), l'errore 407 può apparire anche con credenziali corrette — perché il tuo attuale IP esterno non è incluso nella whitelist sul lato proxy. La soluzione è accedere al pannello di controllo del fornitore e aggiornare l'elenco degli IP consentiti, oppure passare a un'autenticazione tramite login/password invece di IP-whitelist.

Logica di retry e gestione delle eccezioni

Nella pratica, gli errori proxy raramente sono sporadici — soprattutto durante l'estrazione di massa. La corretta architettura del codice dovrebbe prevedere tentativi ripetuti con ritardi e registrazione di ogni errore separatamente.

import time
import requests
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def fetch_with_retry(url, proxies, max_retries=3, backoff=2):
    for attempt in range(1, max_retries + 1):
        try:
            response = requests.get(url, proxies=proxies, timeout=15)
            response.raise_for_status()
            return response
        except requests.exceptions.ProxyError as e:
            logger.warning(f"[Tentativo {attempt}] ProxyError: {e}")
        except requests.exceptions.SSLError as e:
            logger.warning(f"[Tentativo {attempt}] SSLError: {e}")
        except requests.exceptions.HTTPError as e:
            if response.status_code == 407:
                logger.error("407: controlla login/password o whitelist IP del proxy")
                break
            logger.warning(f"[Tentativo {attempt}] HTTPError: {e}")
        except requests.exceptions.Timeout:
            logger.warning(f"[Tentativo {attempt}] Timeout della connessione")

        time.sleep(backoff * attempt)

    raise RuntimeError(f"Impossibile ottenere {url} dopo {max_retries} tentativi")

Questo approccio ti fa risparmiare ore di debug: invece di far crashare lo script al primo intoppo di rete, ottieni un log chiaro con l'indicazione del tipo specifico di errore in ogni tentativo.

Rotazione dei proxy e lotta contro i ban

Se ProxyError e 407 sono spesso legati alle impostazioni del codice, i ban di massa e i rifiuti stabili delle connessioni su domini specifici indicano quasi sempre un problema con la qualità stessa del pool di IP. Per attività come l'estrazione di marketplace (Wildberries, Ozon, Avito) o lavoro con social network, è consigliabile ruotare gli IP per ogni richiesta o attraverso un intervallo specificato.

import random

proxy_pool = [
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
]

def get_random_proxy():
    proxy = random.choice(proxy_pool)
    return {"http": proxy, "https": proxy}

response = requests.get(url, proxies=get_random_proxy(), timeout=15)

Per i parser in produzione, è solitamente più comodo utilizzare proxy con rotazione automatica da parte del fornitore — in questo modo non è necessario mantenere un elenco di IP autonomamente, la rotazione avviene in modo trasparente tramite un endpoint. In questo caso, i proxy residenziali con rotazione sono particolarmente adatti per eludere le protezioni anti-bot su siti con sistemi di rilevamento rigidi, mentre i proxy dei data center sono una buona scelta se la velocità delle richieste è importante e il sito target non blocca aggressivamente i range dei data center.

Tipo di proxy Velocità Rischio di ban Attività
Data center Alta Medio-alto Estrazione veloce di API aperti
Residenziali Media Basso Estrazione di marketplace, social network
Mobile Media-bassa Minimo Lavoro con sistemi anti-bot rigidi

Session, timeout e verifica SSL

Per script a lungo termine (ad esempio, parser ciclici) è consigliabile utilizzare requests.Session() invece di singole chiamate requests.get(). La Session riutilizza le connessioni TCP, il che riduce il carico sul server proxy e diminuisce il numero di ProxyError su grandi volumi di richieste.

session = requests.Session()
session.proxies.update({
    "http": "http://user:password@proxy_host:port",
    "https": "http://user:password@proxy_host:port",
})

for url in urls_list:
    try:
        resp = session.get(url, timeout=15)
        print(resp.status_code, url)
    except requests.exceptions.RequestException as e:
        print(f"Errore su {url}: {e}")

Vale la pena menzionare separatamente il parametro verify. Se stai lavorando con un proxy che sostituisce il certificato SSL (proxy MITM per il debug o alcuni proxy aziendali), requests potrebbe lamentarsi per la non corrispondenza del certificato. Disabilitare completamente la verifica (verify=False) è consigliabile solo in un ambiente di test — in produzione ciò crea il rischio di attacchi "uomo nel mezzo":

# Solo per debug locale!
response = requests.get(url, proxies=proxies, verify=False, timeout=15)

# Il modo corretto è specificare il percorso del certificato proxy attendibile
response = requests.get(url, proxies=proxies, verify="/path/to/proxy_cert.pem")

Checklist di diagnosi degli errori

Se lo script si blocca con un errore proxy, segui questo elenco dall'alto verso il basso — nel 90% dei casi la causa sarà nei primi tre punti.

  1. Controlla lo schema nel dizionario proxies — dovrebbe quasi sempre essere http://, anche per richieste HTTPS.
  2. Testa il proxy direttamente tramite curl -x, per escludere problemi nel codice.
  3. Assicurati che login e password non contengano caratteri speciali non escapati nell'URL.
  4. Controlla se il proxy è legato a un IP specifico (whitelist) invece di autorizzazione tramite login/password.
  5. Aumenta il timeout a 15-30 secondi prima di considerare il proxy non funzionante.
  6. Se l'errore appare solo su un sito specifico, il problema è nella reputazione dell'IP, non nel codice.
  7. Aggiungi logica di retry con ritardi esponenziali invece di tentativi singoli.
  8. Durante l'estrazione di massa, utilizza la rotazione degli IP, non un singolo proxy statico per tutte le richieste.

Conclusione

La maggior parte degli errori proxy in Python requests si riduce a tre cose: uno schema errato nell'URL del proxy (http invece di https), mancanza o formato errato dell'autenticazione, e qualità del pool di IP stesso. ProxyError solitamente indica un problema di connessione o ban dell'IP, SSLError WRONG_VERSION_NUMBER quasi sempre indica uno schema proxy errato, e 407 — un problema con login/password o whitelist.

Se ti imbatti regolarmente in ban e errori 407 durante l'estrazione di marketplace o social network, è opportuno rivedere la fonte stessa del proxy, non solo il codice. Per un'estrazione stabile con una bassa percentuale di blocchi, ti consigliamo di provare proxy residenziali — utilizzano indirizzi IP reali e causano significativamente meno ProxyError su siti protetti. Se invece la priorità è la velocità e il volume delle richieste, piuttosto che eludere sistemi anti-bot complessi, considera i proxy dei data center — sono più economici e più veloci per compiti di estrazione semplici.