Rust reqwest proxy with a login, and tunnel error: unsuccessful
reqwest takes a proxy in one line. What it doesn't do is tell you which status the proxy refused with, or that Proxy::http skips every https:// URL.
In short
Use Proxy::all("http://via.proxies.vision:9000")?.basic_auth(&user, &pass). proxy authorization required is a wrong or missing login; tunnel error: unsuccessful is any other refusal, on our gateway a 403 or 465. SOCKS5 needs the socks feature.
reqwest has three constructors, and they decide which website URLs use the proxy, not how the proxy is reached. Proxy::http applies to http:// URLs only, Proxy::https to https:// URLs only, and Proxy::all to both. We compared the IP each request reached the site from with our own:
The login works both ways: inside the URL, or with .basic_auth(user, pass) on the proxy. basic_auth needs no escaping. In a URL, a password with @ in it wasn’t parsed the way it reads and ended in a 407.
The environment counts too
A client built without .proxy() still uses HTTPS_PROXY and HTTP_PROXY. With HTTPS_PROXY=http://USER:PASS@via.proxies.vision:9000 a plain Client::builder().build() went through our gateway. Call .no_proxy() on the builder when a program must ignore the environment.
Every message, reproduced
reqwest 0.13.5 against via.proxies.vision, https://ipinfo.io as the target. The error chain is each source() joined with ←.
| What we set | Error chain, after “error sending request for url (…) ← client error (Connect) ←” |
|---|---|
| Wrong password, or no login | tunnel error: proxy authorization required |
| Wrong password, http:// URL | no error: Ok(Response) with status 407 |
Username -country-usa | tunnel error: unsuccessful |
| Chicago with country de | tunnel error: unsuccessful |
socks5h:// without the socks feature | tunnel error: failed to create underlying connection ← unsupported scheme socks5h |
| socks5h://, wrong password (feature on) | error connecting to socks proxy ← SOCKS error: credentials not accepted |
| HTTP to the SOCKS port 11000 | tunnel error: unsuccessful |
https:// as the proxy scheme | tunnel error: failed to create underlying connection ← received corrupt message of type InvalidContentType |
“unsuccessful” is three different problems
reqwest names exactly one proxy status, 407. Anything else the proxy answers to CONNECT becomes tunnel error: unsuccessful, and so does talking HTTP to a port that doesn’t speak it. To see the real code, send the same login through curl once: curl -v -x http://via.proxies.vision:9000 -U USER:PASS https://ipinfo.io/ip. Here is what each code means.
403: a value in the username
Targeting goes into the proxy username. Our gateway refuses values it can’t read, such as a three-letter country code or a session label on the rotating port. A key it doesn’t know is ignored without an error.
465: no exit
The login and the username are fine, but no residential exit matches every filter right now. Drop the narrowest one, usually the city.
Match on the error in code
The Debug output of a 407 ends in hyper_util::client::legacy::Error(Connect, ProxyAuthRequired). For http:// URLs there is no tunnel and no error: check response.status() for 407 before you trust the body.
reqwest parses socks5:// and socks5h:// URLs either way, but it can only use them when it was built with the socks feature. Without it, 0.13 says so: unsupported scheme socks5h. reqwest 0.12.28 said tunnel error: unsuccessful instead, which sends people looking for a proxy problem.
reqwest = { version = "0.13", features = ["socks"] }socks5h, so the proxy resolves the name
With the feature on, socks5h://USER:PASS@via.proxies.vision:11000 and socks5:// both returned 200. The h sends the website’s hostname to the proxy instead of looking it up on your machine, so no DNS request leaves from your own connection. A wrong password then reads SOCKS error: credentials not accepted.
A setup that works
A new binary crate (cargo new proxy-demo). Ran unchanged with cargo run on Rust 1.99 and reqwest 0.13.5.
[package]
name = "proxy-demo"
version = "0.1.0"
edition = "2024"
[dependencies]
reqwest = "0.13"
tokio = { version = "1", features = ["full"] }use std::{env, time::Duration};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let user = format!("{}-country-de", env::var("PROXY_USER")?);
let pass = env::var("PROXY_PASS")?;
// Proxy::all covers http:// and https:// URLs. Proxy::http alone lets https:// bypass the proxy.
let proxy = reqwest::Proxy::all("http://via.proxies.vision:9000")?.basic_auth(&user, &pass);
let client = reqwest::Client::builder()
.proxy(proxy)
.timeout(Duration::from_secs(30))
.build()?;
let body = client.get("https://ipinfo.io/json").send().await?.text().await?;
println!("{body}");
Ok(())
}{
"ip": "178.26.154.247",
"city": "Hamburg",
"region": "Hamburg",
"country": "DE",
…One client, one IP
Port 9000 gives every new connection a new exit, but a reqwest Client keeps its connection open: four requests with one client came from one IP, three new clients from three. For a fixed IP on purpose, put a session into the username and use port 10000: USER-country-de-session-job1-lifetime-10.
How do I use a proxy with username and password in reqwest?
What does 'tunnel error: unsuccessful' mean in reqwest?
Why does reqwest say 'unsupported scheme socks5h'?
Why does reqwest not use my proxy for https URLs?
Does reqwest read HTTPS_PROXY?
Keep reading
PHP cURL Proxy With Authentication: CURLOPT_PROXY, 407 and Guzzle
A PHP cURL proxy with username and password: CURLOPT_PROXYUSERPWD, why a 407 isn't always an error, SOCKS5, and the Guzzle setting that skips the proxy.
6 min read · #authentication#errors
aiohttp Proxy Errors: ClientHttpProxyError 407, SOCKS5 and trust_env
aiohttp's ClientHttpProxyError explained, why its message leaks your proxy password, socks5:// without aiohttp-socks, and rotation.
6 min read · #python#errors
curl: (56) CONNECT tunnel failed, response 407 — and git's Proxy CONNECT aborted
What curl's 'CONNECT tunnel failed, response 407' means, every curl proxy exit code we hit, and why git fails through a proxy that works in curl.
6 min read · #errors#authentication
Residential proxies, pay per GB
HTTP and SOCKS5. Country, city and sticky sessions in the username. No subscription.