How to Use a Proxy in Rust: A reqwest Client Guide
A working code example for routing requests through a proxy in Rust, using the reqwest crate's built-in Proxy type.

If you're building an HTTP client in Rust and need to route requests through a proxy, reqwest handles it natively. You don't need a separate crate.
Build a Proxy with reqwest::Proxy::http(), pass it to ClientBuilder::proxy(), and every request made with that client goes through it. That's the whole setup.
Here's the minimal version:
use reqwest::{Client, Proxy};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::builder()
.proxy(Proxy::http("http://proxy-host:proxy-port")?)
.build()?;
let body = client
.get("https://example.com")
.send()
.await?
.text()
.await?;
println!("{body}");
Ok(())
}
That's it. Every request made with client now goes through the proxy.
HTTP-only, HTTPS-only, or both
reqwest gives you three constructors depending on what you want routed:
Proxy::http(url)— proxies HTTP requests onlyProxy::https(url)— proxies HTTPS requests onlyProxy::all(url)— proxies everything, regardless of scheme
Most setups just want everything routed the same way, so Proxy::all() is usually the one you reach for:
let client = Client::builder()
.proxy(Proxy::all("http://proxy-host:proxy-port")?)
.build()?;
If you need different proxies for different destinations; say, one proxy for a specific domain and a fallback for everything else, chain multiple .proxy() calls on the builder, or use Proxy::custom() with a closure that returns the right proxy URL per request.
Adding authentication
Most paid proxies need a username and password. reqwest has a method for exactly this, you don't build the header yourself:
let proxy = Proxy::all("http://proxy-host:proxy-port")?
.basic_auth("username", "password");
let client = Client::builder().proxy(proxy).build()?;
basic_auth() sets the Proxy-Authorization header on every request that goes through this proxy. If your provider expects a non-Basic auth scheme, custom_http_auth() takes a raw header value instead, so you're not locked into Basic if your provider doesn't use it.
Using environment variables instead of hardcoding
If you don't call .proxy() at all, reqwest still checks HTTP_PROXY, HTTPS_PROXY, and NO_PROXY in your environment and uses them automatically:
let client = Client::builder().build()?;
// picks up HTTP_PROXY / HTTPS_PROXY / NO_PROXY if they're set
This is on by default, so it's worth knowing how to turn off if you want a client that never proxies, regardless of what's in the environment:
let client = Client::builder()
.no_proxy()
.build()?;
no_proxy() here disables all proxying on the client, including the automatic environment-variable lookup. That's different from Proxy::no_proxy(), which sets a bypass list on one specific proxy, don't confuse the two.
Turning on SOCKS5 support
reqwest's SOCKS5 support sits behind a Cargo feature, so if your proxy provider gives you a socks5:// address, add the feature first:
[dependencies]
reqwest = { version = "0.12", features = ["socks"] }
Then point Proxy::all() at the SOCKS5 URL the same way you would an HTTP one:
let client = Client::builder()
.proxy(Proxy::all("socks5://proxy-host:proxy-port")?)
.build()?;
Skip this step and you'll get a compile error the moment you try to build a socks5:// proxy URL, not a runtime failure, so it's an easy one to catch early.
Confirming the proxy is actually being used
A 200 OK response isn't proof the proxy is in the request path. If your proxy config is silently ignored, you'll still reach the target site directly and never notice.
Hit an IP-echo endpoint with and without the proxy, and compare:
let body = client
.get("https://api.ipify.org")
.send()
.await?
.text()
.await?;
If the returned IP matches your proxy's IP, not your machine's, the proxy is doing its job. If it matches your own connection, check that the Client you're using is the one you built with .proxy(), it's easy to accidentally send a request through a plain Client::new() left over from earlier in the code.
Handling the errors you'll actually hit
Three errors show up most often once you're running this against a real proxy:
error sending request... error trying to connect: tcp connect error The proxy host or port is wrong, or the proxy is down. Check the address before debugging anything else in your code.
407 Proxy Authentication Required Your credentials are missing or wrong. If a password contains special characters, make sure it's URL-encoded before it goes into the proxy URL string, or pass it through basic_auth() instead of embedding it in the URL.
Requests that hang and eventually time out Set an explicit timeout so a dead proxy fails fast instead of hanging your program:
let client = Client::builder()
.proxy(Proxy::all("http://proxy-host:proxy-port")?)
.timeout(std::time::Duration::from_secs(10))
.build()?;
Without this, a client using a bad proxy can hang far longer than you'd expect, reqwest doesn't apply a request timeout by default.
Putting it together
For most Rust projects, this is enough:
use reqwest::{Client, Proxy};
use std::time::Duration;
fn build_proxy_client(
proxy_url: &str,
username: &str,
password: &str,
) -> Result<Client, reqwest::Error> {
let proxy = Proxy::all(proxy_url)?.basic_auth(username, password);
Client::builder()
.proxy(proxy)
.timeout(Duration::from_secs(10))
.build()
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = build_proxy_client("http://proxy-host:proxy-port", "username", "password")?;
let resp = client.get("https://example.com").send().await?;
println!("status: {}", resp.status());
Ok(())
}
One Client, built once, reused across every request that needs to go through the proxy. No dependencies beyond reqwest itself, plus the socks feature if you're on SOCKS5.
If you're testing this against a real proxy pool rather than a single static proxy, keep in mind that providers billing per successfully authenticated IP expect exactly this kind of username/password setup, so the code above should work against most of them without changes beyond the host, port, and credentials.
