15% off your first purchase with code WELCOME15, up to 25 GB.

See pricing

Integration · HTTP clients

How to use a proxy with Rust reqwest

In reqwest, a proxy is added on the ClientBuilder: .proxy(reqwest::Proxy::all("http://host:port")?.basic_auth(user, pass)). Proxy::all covers HTTP and HTTPS targets, and basic_auth sets the Proxy-Authorization header, which also goes on the CONNECT request for HTTPS sites. A Client holds one proxy configuration, so one client per session or country.

Reviewed 30 Oct 2026

your proxy, as a URL
http://USERNAME_pool-arcfast_country-de:PASSWORD@proxy.arcproxies.io:8000

The short answer

  • Proxy::all(url)?.basic_auth(user, pass); adding any proxy turns off reqwest's automatic system-proxy lookup.
  • Set timeout and connect_timeout: reqwest has no overall timeout by default.
  • A refused tunnel is an error with is_connect() true and "proxy authorization required" in its source chain.
  • SOCKS5 needs the socks feature; use socks5h:// for DNS at the proxy.

Setup

Set up Rust reqwest with a proxy, step by step

  1. 01

    Add the dependencies

    The socks feature is only needed for SOCKS5.

    Cargo.toml
    [dependencies]reqwest = { version = "0.13", features = ["socks"] }tokio = { version = "1", features = ["full"] }
  2. 02

    Build a client and check the exit IP

    The client is cheap to clone and shares its pool; build it once per proxy username.

    src/main.rs
    use std::time::Duration;use reqwest::{Client, Proxy};fn arc_client(username: &str) -> reqwest::Result<Client> {    Client::builder()        .proxy(Proxy::all("http://proxy.arcproxies.io:8000")?.basic_auth(username, "PASSWORD"))        .connect_timeout(Duration::from_secs(10))        .timeout(Duration::from_secs(30))        .build()}#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> {    let client = arc_client("USERNAME_pool-arcfast_country-de")?;    let body = client.get("https://api.ipify.org?format=json").send().await?.text().await?;    println!("{body}");    Ok(())}
  3. 03

    One client per sticky session

    A session handle in the username keeps the exit; the client keeps the connection. Mint a handle per task.

    src/main.rs, continued
    use std::time::{SystemTime, UNIX_EPOCH};fn new_handle() -> String {    // any unique string works; use the rand crate in real code    let n = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos();    format!("{n:08x}")}async fn crawl_listing(start: &str) -> Result<(), reqwest::Error> {    let user = format!("USERNAME_pool-arcfast_country-de_session-{}_sesstime-10", new_handle());    let session = arc_client(&user)?;    for page in 1..=3 {        let res = session.get(format!("{start}?page={page}")).send().await?;        println!("{page} {}", res.status());    }    Ok(())}
  4. 04

    Target a country or city

    Swap the username per market. Cities come from the published list, lowercased without spaces; the examples use the residential pool.

    inside an async fn
    let markets = [    ("de-cologne", "USERNAME_pool-arcfast_country-de_city-cologne"),    ("us-dallas",  "USERNAME_pool-arcfast_country-us_city-dallas"),    ("jp-tokyo",   "USERNAME_pool-arcfast_country-jp_city-tokyo"),];for (market, user) in markets {    let res = arc_client(user)?.get("https://example.com/price/123").send().await?;    println!("{market} {}", res.status());}
  5. 05

    Detect 407, retry the rest

    For an HTTPS target, a refused tunnel fails send() before any response exists; walk the error's source chain to recognise it. For plain HTTP you get a response with status 407. Retry timeouts, 429 and 5xx with backoff on a new session.

    src/main.rs, continued
    use std::error::Error as _;use reqwest::StatusCode;fn is_proxy_auth_error(err: &reqwest::Error) -> bool {    let mut source = err.source();    while let Some(e) = source {        if e.to_string().contains("proxy authorization required") {            return true;        }        source = e.source();    }    false}async fn fetch(url: &str) -> Result<String, Box<dyn std::error::Error>> {    for attempt in 0..4u32 {        let user = format!("USERNAME_pool-arcfast_country-de_session-{}_sesstime-5", new_handle());        match arc_client(&user)?.get(url).send().await {            Ok(res) if res.status() == StatusCode::PROXY_AUTHENTICATION_REQUIRED => {                return Err("proxy rejected the credentials".into())            }            Ok(res) if res.status() != StatusCode::TOO_MANY_REQUESTS && !res.status().is_server_error() => {                return Ok(res.text().await?)            }            Err(e) if is_proxy_auth_error(&e) => return Err("proxy rejected the credentials".into()),            _ => {} // timeout, connection error, 429 or 5xx: retry        }        tokio::time::sleep(std::time::Duration::from_secs(1 << attempt)).await;    }    Err(format!("gave up on {url}").into())}

Proxy::all, Proxy::http and Proxy::https

ConstructorApplies toUse
Proxy::all(url)Every requestThe normal choice for the gateway
Proxy::https(url)Only https:// targetsMixed setups where plain HTTP goes direct
Proxy::http(url)Only http:// targetsRarely useful for scraping

Credentials can also sit in the proxy URL (http://user:pass@host:port); reqwest used them in our test. .basic_auth() avoids percent-encoding the password by hand.

Connection reuse decides when a rotating exit changes

A reqwest Client pools connections (idle ones close after 90 seconds by default), and an HTTPS connection through the gateway is one CONNECT tunnel with one exit. Two requests on one client shared a tunnel in our test. With a rotating username that means one exit per pooled connection; for a new exit per request, use a fresh session handle per request, and for a fixed exit, a sticky session.

With socks5h:// the gateway resolves the hostname, so DNS follows the exit country. Plain socks5:// resolves locally, which can pick a server near you instead.

The problem

Common pitfalls

No overall timeout

reqwest's timeout defaults to none. A stalled response through any proxy then waits indefinitely.

A Client per request

Each new client has an empty pool, so every request pays for a TCP connection, a tunnel and a TLS handshake. Build one per session and clone it.

Expecting HTTPS_PROXY to apply

Calling .proxy(...) disables the automatic system proxy. That is what you want; just do not rely on environment variables as well.

socks5 when you meant socks5h

socks5:// resolves DNS on your machine. For geo-targeted work use socks5h://.

Downloading what you do not need

Every byte through the gateway is billed per GB. reqwest only decompresses responses with its gzip, brotli or deflate features enabled; reducing proxy bandwidth lists more savings.

FAQ

Questions

Something not covered? Write to contact@arcproxies.io.

How do I use an authenticated proxy with reqwest?

Build the client with Client::builder().proxy(reqwest::Proxy::all("http://host:port")?.basic_auth("user", "pass")).build()?. reqwest sends the credentials as Proxy-Authorization, including on the CONNECT request for HTTPS sites.

Does reqwest support SOCKS5 proxies?

Yes, with the socks cargo feature. Use Proxy::all("socks5h://user:pass@host:port") so DNS is resolved at the proxy.

How do I use a different proxy per request in reqwest?

Proxies are set per Client, so keep one client per username (session or market) and choose the client per request. Clients are cheap to clone and share their pool.

How do I detect a 407 proxy error in reqwest?

For HTTPS targets send() returns an error with is_connect() true; its source chain contains "proxy authorization required". For plain-HTTP targets the response status is StatusCode::PROXY_AUTHENTICATION_REQUIRED.

Sources & review

Last reviewed by the ARC Proxies team. Prices are read from the live catalog at build time.

  1. [1]docs.rs: reqwest::Proxy (0.13)
  2. [2]docs.rs: reqwest::ClientBuilder (0.13)
  3. [3]docs.rs: reqwest::Error
  4. [4]docs.rs: reqwest feature flags

Next

What people build with it

Use caseProxies for web scraping and data collectionA second provider that just works — Scrapy, requests, Crawlee or Playwright, self-serve, no sales call, bandwidth that never expires.Use caseProxies for AI and LLM training-data collectionCollect public web text, images and product data at corpus scale from residential and ISP IPs, paid per gigabyte with no monthly commitment.Use caseProxies for cybersecurity and threat intelligenceInvestigate phishing kits, typosquats and malicious infrastructure from an IP that is not your company's, and see the payload the target audience is served.GlossaryHTTP CONNECTHTTP CONNECT is the HTTP request method a client sends to a proxy to ask for a tunnel to a host and port; once the proxy answers with a 2xx status it blindly forwards bytes in both directions, which is how HTTPS passes through an HTTP proxy with TLS intact end to end (RFC 9110, section 9.3.6).GlossarySticky sessionA sticky session is a setting on a rotating proxy that keeps the same exit IP address for a chosen period, usually by attaching a session identifier to the connection, so a multi-step task appears to come from one address from start to finish.GuideHow to reduce proxy bandwidthCut proxy bandwidth by fetching HTML or JSON instead of rendering, blocking images and fonts, using compression, conditional requests and local caching.IntegrationGoRoute Go's net/http through an authenticated proxy: Transport.Proxy with url.UserPassword, a sticky session per request context, SOCKS5 and 407 checks.IntegrationcurlSend curl through an authenticated proxy with -x and -U, check the exit IP, test sticky sessions and countries, use socks5h, and read 407 failures.

Integrations

Other http clients

Python requestsUse an authenticated proxy with Python requests: the proxies argument, sticky sessions per task, geo-targeting, SOCKS5 and retries that skip 407 errors.HTTPXConfigure an authenticated proxy in HTTPX 0.28: the proxy= argument that replaced proxies=, mounts per domain, async clients, SOCKS5 and error handling.aiohttpUse an authenticated proxy with aiohttp: proxy= per request or per session, why proxy_auth is deprecated, SOCKS5 through aiohttp-socks, and 407 handling.AxiosUse an authenticated proxy with Axios in Node.js: the proxy option on 1.16.1+, https-proxy-agent for older versions, sticky sessions, SOCKS5 and 407s.Node.js fetchNode's built-in fetch has no proxy option. Use undici's ProxyAgent as the dispatcher, or NODE_USE_ENV_PROXY, with sticky sessions, SOCKS5 and 407 handling.curlSend curl through an authenticated proxy with -x and -U, check the exit IP, test sticky sessions and countries, use socks5h, and read 407 failures.GoRoute Go's net/http through an authenticated proxy: Transport.Proxy with url.UserPassword, a sticky session per request context, SOCKS5 and 407 checks.RubyUse an authenticated proxy in Ruby: Net::HTTP proxy arguments, Faraday proxy options, sticky sessions per task, geo-targeting and 407 handling.PHP GuzzleRoute PHP Guzzle and plain cURL through an authenticated proxy: the proxy option, sticky sessions, geo-targeting, retries that stop on a 407.Java HttpClientUse an authenticated proxy with java.net.http.HttpClient: ProxySelector, Authenticator, the Basic-over-CONNECT setting, sticky sessions and 407s.C# HttpClientUse an authenticated proxy in .NET: WebProxy and NetworkCredential on SocketsHttpHandler, a client per sticky session, SOCKS5 and 407 handling.wgetRun GNU Wget through an authenticated proxy: https_proxy, --proxy-user, a .wgetrc for credentials, sticky sessions for mirrors and exit codes.PostmanSend Postman requests through an authenticated proxy: the custom proxy settings, Proxy auth, checking the exit IP, sessions, countries and Newman.DockerGive Docker containers and builds an authenticated proxy: HTTP_PROXY, HTTPS_PROXY and NO_PROXY, build args, Compose, and why the daemon proxy differs.

Get started

Get credentials and paste them in.

Sign up, buy a gigabyte, generate a username in the dashboard. Code WELCOME15 takes 15% off your first purchase; bandwidth never expires.