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.

5 min readTested on reqwest 0.13.5 and 0.12.28 · Rust 1.99
reqwest reports a proxy's 407 as proxy authorization required, but a 403 and a 465 both as tunnel error: unsuccessful.What the proxy answered, and what reqwest tells you407tunnel error: proxy authorization required403tunnel error: unsuccessful465tunnel error: unsuccessful200Ok(Response { status: 200, … })reqwest 0.13.5 and 0.12.28 · October 20, 2026

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.

Proxy::all(…)?.basic_auth(user, pass)features = ["socks"]

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:

own IPProxy::http("http://USER:PASS@via.proxies.vision:9000"), https:// URLnot used, no error
proxiedProxy::https(…), https:// URLhttps only
proxiedProxy::all(…)every URL

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 setError chain, after “error sending request for url (…) ← client error (Connect) ←”
Wrong password, or no logintunnel error: proxy authorization required
Wrong password, http:// URLno error: Ok(Response) with status 407
Username -country-usatunnel error: unsuccessful
Chicago with country detunnel error: unsuccessful
socks5h:// without the socks featuretunnel 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 11000tunnel error: unsuccessful
https:// as the proxy schemetunnel error: failed to create underlying connection ← received corrupt message of type InvalidContentType
Fig. 1 — Display of the error and its sources, October 20, 2026. 403 and 465 are our gateway’s statuses.

“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.

toml
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.

Cargo.toml
[package]
name = "proxy-demo"
version = "0.1.0"
edition = "2024"

[dependencies]
reqwest = "0.13"
tokio = { version = "1", features = ["full"] }
src/main.rs
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(())
}
txt
{
  "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?
reqwest::Proxy::all("http://host:port")?.basic_auth(user, pass), then pass it to Client::builder().proxy(). A login inside the URL (http://user:pass@host:port) works too, but needs URL-encoding for characters like @.
What does 'tunnel error: unsuccessful' mean in reqwest?
The proxy refused to open the tunnel with a status other than 407. reqwest doesn't say which one. On our gateway that was 403 (a value in the username it can't read) or 465 (no exit matches the filters). Send the same login through curl -v once to see the code.
Why does reqwest say 'unsupported scheme socks5h'?
SOCKS support is behind a Cargo feature. Add reqwest = { version = "0.13", features = ["socks"] }. reqwest 0.12 without the feature didn't name the problem; it said 'tunnel error: unsuccessful'.
Why does reqwest not use my proxy for https URLs?
Proxy::http only applies to http:// URLs. With it, https:// requests went out directly from our own IP, without an error. Use Proxy::all, or Proxy::https for https:// URLs.
Does reqwest read HTTPS_PROXY?
Yes. A client built without .proxy() uses HTTPS_PROXY and HTTP_PROXY from the environment, login included. Call .no_proxy() on the builder if a script must ignore them.

Residential proxies, pay per GB

HTTP and SOCKS5. Country, city and sticky sessions in the username. No subscription.