Configurare un proxy in HTTPX: autenticazione, ambiente e timeout
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.
- Usi esattamente il client che importerà davvero. Questa guida ha usato HTTPX 0.28.1, httpcore 1.0.9 e Python 3.14.6.
- Costruisca un
Clientconproxy=impostato sull'URL del proxy HTTP. Lascitrust_env=Falsementre prova il percorso esplicito, così variabili di shell rimaste non possono rubare il percorso. - 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>/conauthorized: true. - 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.
- Tenga l'URL del proxy su
http://127.0.0.1:<port>anche quando la destinazione èhttps://httpx-lab.origin:<port>/. - Si fidi dell'origine con
ssl.create_default_context(cafile=ca.pem)e passi quel context comeverify=. Non spenga la verifica. - Confermi che il log del proxy mostri
CONNECT httpx-lab.origin:<https-port>prima di festeggiare un 200. - 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.
- Metta utente e password del proxy in
httpx.Proxy(..., auth=(user, password))quando la password ha caratteri riservati. - Tenga
auth=dell'origine per il sito, non per il proxy. - Tratti HTTP 407 e
ProxyErrorsu CONNECT come esiti diversi. Registri la classe che ha davvero catturato. - 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.
- Esporti
HTTP_PROXYeHTTPS_PROXYsull'URL proxy encoded. ImpostiNO_PROXYsu una stringa vuota quando la destinazione deve usare il proxy. - Costruisca
Client(trust_env=True)senza argomentoproxy=. La richiesta del lab ha restituito 200 e solo il proxyalphaha registrato il GET. Il proxybravoè rimasto inattivo. - 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. - 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.
| Fallimento | Che cosa ha sollevato il lab 0.28.1 | Che cosa non è |
|---|---|---|
| Proxy HTTP, Basic assente o sbagliata | Response status 407 | Un'eccezione CONNECT |
| HTTPS CONNECT, Basic assente o sbagliata | ProxyError ("407 Proxy Authentication Required") | Una pagina 407 dell'origine |
| HTTPS di lab senza la CA del lab | ConnectError / SSLCertVerificationError dopo CONNECT | Prova che CONNECT non sia mai successo |
| Porta loopback chiusa | ConnectError / ConnectionRefusedError | ConnectTimeout |
| Bind without listen su Darwin | ConnectTimeout (attesa specifica della fixture) | Un rifiuto, o un RTT WAN |
| Corpo ritardato dopo il primo byte | ReadTimeout mentre si itera iter_bytes() | Un fallimento di connect |
Origine /status/503 con retries=2 | Status 503, un colpo all'origine | Un retry applicativo |

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 vende proxy residenziali, mobile, ISP e datacenter sotto un unico abbonamento mensile da $30. Pagamenti: carte, bonifico, AliPay, PayPal.

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).
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?
Perché usare http:// per un proxy che gestisce HTTPS?
In che cosa le credenziali del proxy differiscono da auth=?
trust_env=False disabilita la verifica dei certificati?
Perché il client solleva ReadTimeout dopo che gli header sono arrivati?
HTTPTransport ritenta 429 o 503?
Articoli correlati
Che cosa sono i proxy residenziali e come funzionano
I proxy residenziali escono da indirizzi ISP consumer. Qui si spiegano pool, sessioni sticky, che cosa vedono i siti e che cosa chiedere a un fornitore prima di comprare.
4 minI migliori proxy per il web scraping nel 2026: una lista di controllo per chi compra
Un acquisto scraping nel 2026 parte dalla destinazione, non dal fornitore. Usi questa lista per origine, sessione, geo, fatturazione e un test live prima di salire di scala.
5 minProxy sticky e a rotazione: come funziona il controllo di sessione
Proxy sticky e a rotazione: come funzionano la rotazione a ogni richiesta e quella a timer, quando una sessione sticky batte un IP nuovo, come i cookie interagiscono con il cambio di IP e come testare un ID di sessione da sé.

