Configurare un proxy in HTTPX: autenticazione, ambiente e timeout

15 minuti di lettura

Imposti un proxy HTTPX con proxy=, passi le credenziali, tenga HTTPS su CONNECT e distingua i default dell'ambiente da mounts e timeout espliciti.

Questa guida è per gli sviluppatori Python che hanno bisogno che HTTPX 0.28.1 esca attraverso un proxy HTTP o SOCKS5. I controlli editoriali privati hanno usato origini loopback e un proxy CONNECT fittizio, non un account di un fornitore. Niente chiavi gratuite, e Top Proxy non vende IP. Quei controlli provano solo quel percorso di laboratorio. Non sono un download pubblico.

01Instradi una richiesta con proxy=

Il client installato accetta proxy= su httpx.get e su httpx.Client. Le versioni precedenti di HTTPX accettavano anche un mapping proxies= da pattern URL a URL proxy. Era HTTPX stesso, non un dict solo di Requests. HTTPX 0.28.0 (28 novembre 2024) ha rimosso l'argomento deprecato proxies. Questa guida resta su 0.28.1 proxy= e mounts=. La forma a un solo proxy è una stringa URL o un oggetto httpx.Proxy.

I proxy HTTP (consultati il 14 ottobre 2026) dicono di passare quell'URL all'init del client o sugli helper di primo livello. Un singolo proxy="http://127.0.0.1:PORT" invia sia HTTP sia HTTPS attraverso quell'hop. HTTPS non ha bisogno di un URL proxy https://. Lo schema della destinazione e lo schema del proxy sono due impostazioni diverse.

  1. Usi esattamente il client che importerà davvero. Questa guida ha usato HTTPX 0.28.1, httpcore 1.0.9 e Python 3.14.6.
  2. Costruisca un Client con proxy= impostato sull'URL del proxy HTTP. Lasci trust_env=False mentre prova il percorso esplicito, così variabili di shell rimaste non possono rubare il percorso.
  3. Recuperi un'origine HTTP che controlla. Legga il log del proxy, non solo il codice di stato. Il forwarder fittizio del lab ha registrato GET http://httpx-lab.origin:<port>/ con authorized: true.
  4. Chiuda il client con un context manager. Quello chiude il pool di transport. Un client dimenticato lascia un socket aperto, non rende lo script più veloce.

La baseline diretta conta. Dopo aver pulito HTTP_PROXY, HTTPS_PROXY, ALL_PROXY e NO_PROXY in un processo figlio, un client senza proxy= ha colpito l'origine HTTP e il proxy fittizio è rimasto inattivo. Questo è il controllo senza proxy. Non le dice che cosa farebbe HTTPX se quelle variabili fossero ancora impostate. Quello arriva dopo.

Se le servono due uscite, non metta due URL in proxy=. Usi mounts con HTTPTransport(proxy=...) per pattern, e metta verify, trust_env e retries su ciascun transport. L'argomento verify= a livello client non è stato ereditato in questa build. Un mount che ereditava solo Client(verify=ctx) ha fallito TLS con ConnectError / SSLCertVerificationError: unable to get local issuer certificate. La stessa richiesta è riuscita quando il transport stesso ha ricevuto verify=ctx.

02Scelga un proxy HTTP per una destinazione HTTPS

La gente chiama questo un «proxy HTTPS» perché il sito è https://. L'hop davanti è comunque un proxy HTTP che parla CONNECT. La documentazione lo segnala come trabocchetto: la chiave https:// in un dict di mounts è lo schema della destinazione, e l'URL del proxy davanti è di solito http://.

Inoltro e tunneling non sono lo stesso meccanismo. Per http:// il proxy fa la richiesta all'origine e restituisce i byte dell'origine. Per https:// il client invia CONNECT host:443, il proxy apre TCP e TLS gira tra client e origine attraverso quel tunnel. Un 200 finto da CONNECT senza un tunnel di byte funzionante non è proxying HTTPS. Il lab ha inoltrato corpi origine reali su HTTP e byte TLS reali dopo CONNECT.

  1. Tenga l'URL del proxy su http://127.0.0.1:<port> anche quando la destinazione è https://httpx-lab.origin:<port>/.
  2. Si fidi dell'origine con ssl.create_default_context(cafile=ca.pem) e passi quel context come verify=. Non spenga la verifica.
  3. Confermi che il log del proxy mostri CONNECT httpx-lab.origin:<https-port> prima di festeggiare un 200.
  4. Ripeta una volta con lo store CA di default e senza la CA del lab. Vuole un fallimento TLS che ha comunque registrato CONNECT.

Quell'ultimo passo è quello che gli operatori saltano. In questa esecuzione CONNECT è riuscito, poi HTTPX ha sollevato ConnectError che avvolgeva SSLCertVerificationError: unable to get local issuer certificate. Il tunnel andava. Il certificato no. Una pagina del browser Il mio IP avrebbe mostrato l'uscita del browser, non questo client. Usi il controllo proxy per vedere se un host risponde HTTP o SOCKS5 prima di puntarci HTTPX.

SOCKS5 è un terzo percorso. pip install httpx[socks] ha tirato socksio 1.0.0. httpx.Proxy in 0.28.1 accetta socks5 e socks5h. Contro una fixture SOCKS5 controllata, socks5://127.0.0.1:<port> verso http://127.0.0.1:<origin>/ ha inviato ATYP ipv4. Lo stesso client con socks5h:// e una destinazione hostname ha inviato ATYP domain httpx-lab.origin. Usare socks5:// con quell'hostname ha inviato anch'esso ATYP domain. Questa build non ha risolto in locale l'hostname per poi inviare un indirizzo IPv4, come fanno alcuni setup curl socks5. Non copi le dicerie socks5h da Requests in HTTPX. Misuri l'handshake.

03Imposti l'autenticazione del proxy

Le credenziali del proxy non sono auth=. auth= è l'autenticazione verso l'origine. Le mescoli e passerà un'ora a fare debug dell'hop sbagliato.

La pagina dei proxy mette lo userinfo nell'URL del proxy. L'API documenta anche httpx.Proxy(url, auth=(user, password)). Entrambe hanno funzionato nel lab. La password fittizia includeva @, :, /, # e %. Quei caratteri stanno nello userinfo solo dopo la codifica URL componente per componente, o nella tupla auth= su Proxy, che non passa dal parsing URL. Log e figure hanno oscurato il valore. Non stampi Proxy-Authorization quando fa debug.

Credenziali assenti e sbagliate non sono un solo comportamento.

Sull'inoltro HTTP, una richiesta senza utente proxy ha restituito HTTP 407 dalla fixture. HTTPX l'ha esposto come status_code == 407. Una password sbagliata ha fatto lo stesso. La fixture ha registrato authReason: missing e authReason: wrong. Nessuna eccezione. Se il suo wrapper in seguito lancia, tenga il 407.

Su HTTPS CONNECT, le stesse credenziali fittizie assenti o sbagliate hanno sollevato httpx.ProxyError con messaggio 407 Proxy Authentication Required. Non c'era un oggetto response da leggere. CONNECT è un setup di tunnel. Un 407 lì è un fallimento di trasporto, non una pagina dell'origine.

Credenziali corrette hanno restituito origine 200 su entrambi i percorsi. L'origine non ha mai visto Proxy-Authorization. Quell'header resta sull'hop del proxy. Se le serve anche Basic sull'origine, invii auth=(origin_user, origin_pass) sul client. La route /origin-auth del lab ha restituito 401 senza di esso e 200 con entrambi gli hop autenticati.

  1. Metta utente e password del proxy in httpx.Proxy(..., auth=(user, password)) quando la password ha caratteri riservati.
  2. Tenga auth= dell'origine per il sito, non per il proxy.
  3. Tratti HTTP 407 e ProxyError su CONNECT come esiti diversi. Registri la classe che ha davvero catturato.
  4. Rifiuti l'idea che «funziona in curl» dimostri che HTTPX ha inviato lo stesso header. Lo provi su questo client.

I prodotti residenziali documentano lo stesso spacco. Oxylabs Residential Proxies (consultati il 14 ottobre 2026) elencano HTTP, HTTPS, HTTP/3 e SOCKS5, con username/password o allowlist IP, e ingresso pr.oxylabs.io:7777 usando customer-USERNAME:PASSWORD. È una forma di endpoint documentata, non un test del loro pool. L'avvio rapido residenziale Decodo (stesso giorno) mostra gate.decodo.com:7000 e un mapping Python che invia sia http sia https attraverso un solo URL di proxy HTTP. Il catalogo inglese elenca ancora quel prodotto come smartproxy-com. Il nome pubblico sulla documentazione è Decodo. Il path non è cambiato.

04Capisca i default dell'ambiente e i mounts

HTTPX legge l'ambiente di default. Le variabili d'ambiente (consultate il 14 ottobre 2026) elencano HTTP_PROXY, HTTPS_PROXY, ALL_PROXY e NO_PROXY, più SSL_CERT_FILE e SSL_CERT_DIR. trust_env=False spegne tutto quel set. Non significa «salta i controlli dei certificati».

Il processo figlio del lab è partito con quelle variabili rimosse, più i resti CGI, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE e SSLKEYLOGFILE. Ha usato un file NETRC privato vuoto. Il HOME della workstation è rimasto intatto. Ogni esperimento d'ambiente partiva da quella baseline e iniettava solo valori dummy nominati.

Il bypass loopback le mentirà. I client HTTP saltano spesso 127.0.0.1. I casi env-proxy hanno usato l'hostname httpx-lab.origin e NO_PROXY="", con una patch getaddrinfo circoscritta che mappa quel nome su loopback. Quella patch è comportamento di resolver iniettato. Non è una misura di DNS pubblico.

  1. Esporti HTTP_PROXY e HTTPS_PROXY sull'URL proxy encoded. Imposti NO_PROXY su una stringa vuota quando la destinazione deve usare il proxy.
  2. Costruisca Client(trust_env=True) senza argomento proxy=. La richiesta del lab ha restituito 200 e solo il proxy alpha ha registrato il GET. Il proxy bravo è rimasto inattivo.
  3. Ripeta con NO_PROXY=httpx-lab.origin. L'origine ha comunque restituito 200. Alpha non ha visto quel GET. Questo è bypass, ed è un caso dedicato, non un effetto collaterale da assumere.
  4. Ripeta con le stesse variabili d'ambiente e trust_env=False. La richiesta è andata diretta. Alpha non ha visto nulla.

proxy= esplicito ha vinto su un ambiente in conflitto. Con alpha in HTTP_PROXY e proxy= puntato a bravo, bravo ha preso il GET e alpha no. Lo status 200 da solo non le avrebbe detto quale hop era usato. Due identità strumentate sì.

trust_env=False ha anche impedito a SSL_CERT_FILE di diventare lo store CA del client. Con la CA del lab in SSL_CERT_FILE e trust_env=True, HTTPS verso l'origine del lab ha restituito 200. Con trust_env=False e verify=True di default, lo stesso URL ha sollevato ConnectError / SSLCertVerificationError. Passare verify=ssl.create_default_context(cafile=ca.pem) su quel client trust_env=False ha ripristinato 200. La verifica è restata accesa. È sparita solo la lookup dall'ambiente.

transport= e mounts= sono argomenti del costruttore diversi. HTTPX 0.28.1 imposta allow_env_proxies = trust_env and transport is None. Un oggetto transport= personalizzato salta i mounts proxy dall'ambiente. Un dict mounts= no. Il client costruisce prima i mounts d'ambiente, poi aggiorna quella mappa con i suoi mounts. I pattern URL più specifici vincono. http:// da HTTP_PROXY batte all://, quindi Client(mounts={"all://": some_transport}, trust_env=True) può ancora inviare le destinazioni HTTP attraverso il proxy ereditato. Imposti trust_env=False quando il routing d'ambiente deve essere escluso. Metta proxy= e verify= su ciascun HTTPTransport montato. Il verify= a livello client continua a non copiarsi su quei transport. Quell'ultima mancanza è come il fallimento della CA non fidata rientra.

05Provi il ciclo di vita sync e async

Sync e async sono due client. Condividono la forma proxy=. Non condividono un connection pool.

with httpx.Client(proxy=..., verify=ctx) as client: è la forma sync. La chiusura avviene all'uscita del context. async with httpx.AsyncClient(proxy=..., verify=ctx) as client: è la forma async. Fa await client.get(...). Mescolare get senza await dentro il client async è un bug, non un timeout.

Il caso async in questo lab ha usato AsyncClient con lo stesso URL di proxy HTTP e il context SSL del lab. await client.get(https_url) ha restituito 200 con il marchio dell'origine HTTPS. Il proxy ha registrato CONNECT. È un tunnel async riuscito. Non è un benchmark di concorrenza e non dice nulla sul riuso del pool sotto carico.

I transport montati hanno bisogno dello stesso percorso di chiusura. Un HTTPTransport creato a mano è chiuso dal client se il client lo possiede tramite mounts. Se tiene viva una variabile transport tra client, chiami transport.close() in finally. Il lab l'ha fatto sui casi retry e mount.

I retry stanno sul transport. I transport (consultati il 14 ottobre 2026) dicono che HTTPTransport(retries=...) ritenta ConnectError e ConnectTimeout. Non ritenta 429 o 503. Il lab ha impostato retries=2 e ha recuperato /status/503. L'origine ha contato un colpo. HTTPX ha restituito 503. Non è urllib3 Retry. Non incolli le dicerie su allowed_methods in HTTPX.

06Diagnostichi fallimenti di connessione, lettura del corpo e TLS

Nomini l'eccezione che ha catturato. HTTPX ha una gerarchia vera. Collassare tutto a «errore proxy» nasconde l'hop che è fallito.

Un listener loopback chiuso ha prodotto ConnectError che avvolgeva ConnectionRefusedError: [Errno 61] Connection refused. È un rifiuto. Non è un connect timeout. Su questo kernel Darwin, un socket bindato e tenuto ma mai messo in listen non ha rifiutato. HTTPX ha sollevato ConnectTimeout che avvolgeva TimeoutError dopo la scadenza connect configurata. Quell'attesa è etichettata come comportamento di fixture iniettata per bind-without-listen. Non è una misura della rete pubblica.

I read timeout riguardano i chunk dopo che la response è partita. L'origine /slow-body ha inviato gli header completi subito, ha scritto e flushato un byte del corpo, poi ha aspettato più di Timeout(read=0.45). In streaming del corpo, HTTPX ha restituito status 200, l'evento first-byte è scattato, è arrivato un byte, poi ReadTimeout che avvolgeva TimeoutError. Gli header erano già arrivati. ConnectError sarebbe stata la classe sbagliata. ConnectTimeout sarebbe stata la classe sbagliata. Se chiama solo response.text senza streaming, va comunque in timeout, ma perde la prova che gli header erano arrivati.

I fallimenti TLS dopo CONNECT sono restati ConnectError con SSLCertVerificationError. Il proxy aveva già accettato il tunnel. Mettere verify=False lo nasconderebbe. Non lo faccia.

Usi proxy mobili o proxy residenziali quando il lavoro è un'uscita assegnata da un ISP. Supporto di protocollo e origine dell'IP sono dimensioni diverse. Questo lab non ha mai preteso un paese, un tasso di successo o un account.

FallimentoChe cosa ha sollevato il lab 0.28.1Che cosa non è
Proxy HTTP, Basic assente o sbagliataResponse status 407Un'eccezione CONNECT
HTTPS CONNECT, Basic assente o sbagliataProxyError ("407 Proxy Authentication Required")Una pagina 407 dell'origine
HTTPS di lab senza la CA del labConnectError / SSLCertVerificationError dopo CONNECTProva che CONNECT non sia mai successo
Porta loopback chiusaConnectError / ConnectionRefusedErrorConnectTimeout
Bind without listen su DarwinConnectTimeout (attesa specifica della fixture)Un rifiuto, o un RTT WAN
Corpo ritardato dopo il primo byteReadTimeout mentre si itera iter_bytes()Un fallimento di connect
Origine /status/503 con retries=2Status 503, un colpo all'origineUn retry applicativo
Tabella delle asserzioni dal lab loopback HTTPX 0.28.1 del 14 ottobre 2026

La figura è la tabella delle asserzioni di questa esecuzione. Non è un'immagine di copertina e non è uno screenshot di una UI di controllo.

Il blocco successivo è uno schizzo API schematico. Porte, path della CA e password sono placeholder da sostituire con valori che controlla. Non è un bundle di fixture pubblico e non girerà da una directory vuota.

import httpx
import ssl

# PLACEHOLDERS. Do not treat these literals as lab values.
ctx = ssl.create_default_context(cafile="PATH/TO/LAB_CA.pem")
proxy = httpx.Proxy(
    "http://127.0.0.1:PROXY_PORT",
    auth=("PROXY_USER", "PROXY_PASSWORD"),
)
timeout = httpx.Timeout(5.0, connect=2.0, read=0.45)

with httpx.Client(proxy=proxy, verify=ctx, trust_env=False, timeout=timeout) as client:
    http_response = client.get("http://TARGET_HOST:HTTP_PORT/")
    https_response = client.get("https://TARGET_HOST:HTTPS_PORT/")

Le classi di eccezione 0.28.1 e gli esiti CONNECT contro 407 in questo articolo arrivano da una fixture editoriale privata. Quella fixture non è pubblicata qui. Installi un client che può importare con python -m pip install "httpx[socks]==0.28.1", che dipende da httpcore 1.* e, per SOCKS, socksio. Metta host, porta e credenziali del suo proxy HTTP nei placeholder, usando httpx.Proxy(..., auth=(user, password)) quando la password ha caratteri riservati. Punti l'URL HTTP a un'origine che controlla per l'inoltro, e l'URL HTTPS a un'origine che controlla per CONNECT poi TLS. Per HTTPS, passi verify= come ssl.create_default_context(cafile=...) con un file CA che già si fida per quell'origine. Tenga trust_env=False mentre prova il percorso proxy= esplicito. Non imposti verify=False.

07Scelga un endpoint documentato

Una volta che il percorso del client è onesto, scelga un host di ingresso che documenti inoltro HTTP e CONNECT. Le pagine inglesi del catalogo di Oxylabs e Decodo (ancora elencato come smartproxy-com) hanno restituito URL self-canonical live il 14 ottobre 2026. La loro documentazione ufficiale descrive gateway HTTP con username/password o IP in allowlist. È aderenza dell'endpoint sulla carta. Questo articolo non ha fatto login in nessuno dei due prodotti e non li classifica.

Oxylabs — proxy residenziali e datacenter
Oxylabs — proxy residenziali e datacenter4.0771

Oxylabs vende proxy residenziali, mobile, ISP e datacenter sotto un unico abbonamento mensile da $30. Pagamenti: carte, bonifico, AliPay, PayPal.

10 paesi
0,45 USD
per 1 GB
Decodo (Smartproxy) — proxy residenziali e datacenter
Decodo (Smartproxy) — proxy residenziali e datacenter4.22118

Decodo (ex Smartproxy) vende proxy residenziali e datacenter. Residenziali: $4/GB senza piano mensile, o pacchetti mensili da $3.75/GB per 3 GB. Pagamenti: carte, PayPal, Alipay, Google/Apple Pay, crypto (con limiti).

83 paesi
0,38 USD
per 1 GB

Se le serviva solo una conferma nel browser, resti su Il mio IP. Se le serviva HTTPX, si fidi del log del proxy e della classe di eccezione di questa libreria. I due sono in disaccordo più spesso di quanto ammettano i post del blog.

Come si imposta un proxy HTTPX?

Passi proxy= a httpx.Client o httpx.get. Usi una stringa URL o httpx.Proxy. HTTPX 0.28.1 non ha un argomento proxies=. HTTPX ha rimosso quel mapping deprecato in 0.28.0. Per il routing per schema, monti oggetti HTTPTransport che ricevono ciascuno i propri argomenti proxy e verify. mounts= continua a unire i pattern d'ambiente quando trust_env=True. Usi transport= o trust_env=False quando l'HTTP_PROXY ereditato non deve applicarsi.

Perché usare http:// per un proxy che gestisce HTTPS?

L'hop del proxy è di solito HTTP in chiaro. Le destinazioni HTTPS usano CONNECT attraverso quell'hop, poi TLS verso l'origine. La chiave https:// nei mounts è lo schema della destinazione. L'URL del proxy davanti resta http:// a meno che il prodotto non parli davvero TLS verso il proxy stesso.

In che cosa le credenziali del proxy differiscono da auth=?

Lo userinfo del proxy o httpx.Proxy(auth=(user, password)) inviano Proxy-Authorization al proxy. auth= è l'autenticazione verso l'origine. Il lab ha restituito proxy 407 o ProxyError quando le credenziali del proxy erano sbagliate, e origine 401 quando mancavano solo le credenziali dell'origine.

trust_env=False disabilita la verifica dei certificati?

No. trust_env=False impedisce a HTTPX di leggere HTTP_PROXY, NO_PROXY, SSL_CERT_FILE e SSL_CERT_DIR. La verifica TLS resta accesa. HTTPX non carica le credenziali dell'origine da netrc perché trust_env è true. Se passa httpx.NetRCAuth lei stessa, quell'auth si applica comunque con trust_env=False. Passi ssl.create_default_context(cafile=...) come verify= quando le serve una CA privata. verify=False non è lo switch per la gestione dell'ambiente.

Perché il client solleva ReadTimeout dopo che gli header sono arrivati?

Il read timeout è una scadenza per chunk del corpo. L'origine del lab ha inviato gli header e un byte del corpo, poi si è fermata. Lo streaming ha restituito status 200, ha consegnato quel byte, poi ha sollevato ReadTimeout. Non è ConnectTimeout e non è un fallimento nel parlare al proxy.

HTTPTransport ritenta 429 o 503?

No. retries su HTTPTransport ritenta solo ConnectError e ConnectTimeout. Con retries=2 l'origine del lab /status/503 è stata colpita una volta e HTTPX ha restituito 503. Usi un'altra libreria se le servono retry sullo status.