Reqwest via httpS proxy fails

In a simple setup to test reqwest with different proxies (http, https, socks5) and backends (native-tls, rust-tls), I get persistent errors with HTTPS proxies. With native-tls:

What doesn’t work:

  1. HTTPS requests via HTTP and HTTPS proxies

What works:

  1. HTTP requests via HTTP and HTTPS proxies
  2. HTTP and HTTPS requests via SOCKS5 proxies
  3. HTTP and HTTPS requests without proxies

With rustls-tls, in addition to the above, I also get failing HTTP requests via SOCKS5 proxies.

When I try openssl s_client, it works.

When I use another python tool (httpie), everything works.

I am not sure if it is my configuration or reqwest itself. Any help would be greatly appreciated!

FYI:

Log (redacted)

[2025-11-06T12:03:39Z DEBUG reqwest::connect] starting new connection: https://thisismyip.com/
[2025-11-06T12:03:39Z DEBUG reqwest::connect] proxy(http://188.114.96.138/) intercepts 'https://thisismyip.com/'
[2025-11-06T12:03:39Z TRACE reqwest::connect] tunneling HTTPS over proxy
[2025-11-06T12:03:39Z TRACE reqwest::retry] shouldn't retry!
[2025-11-06T12:03:39Z ERROR hello_world] resp_https (via http proxy). CheckProxyError: error sending request for url (https://thisismyip.com/).

[2025-11-06T12:03:40Z DEBUG reqwest::connect] starting new connection: https://thisismyip.com/
[2025-11-06T12:03:40Z DEBUG reqwest::connect] proxy(http://188.114.96.138/) intercepts 'https://thisismyip.com/'
[2025-11-06T12:03:40Z TRACE reqwest::connect] tunneling HTTPS over proxy
[2025-11-06T12:03:40Z TRACE reqwest::retry] shouldn't retry!
[2025-11-06T12:03:40Z ERROR hello_world] resp_https (via HTTPS proxy). CheckProxyError: error sending request for url (https://thisismyip.com/).

Cargo.toml

[package]
name = "hello_world"
version = "0.1.0"
edition = "2024"

[dependencies]
reqwest = { version = "0.12.24", features = ["native-tls", "socks"] }
tokio = { version = "1.48.0", features = ["full"] }
log = "0.4.28"
env_logger = "0.11.8"
dotenvy = "0.15.7"

user@laptop:~$ openssl s_client -connect 84.17.47.148:9002
Connecting to 84.17.47.148
CONNECTED(00000003)
Can't use SSL_get_servername
depth=2 C=US, O=Internet Security Research Group, CN=ISRG Root X1
verify return:1
depth=1 C=US, O=Let's Encrypt, CN=R13
verify return:1
depth=0 CN=amsterdam-rack401.nodes.gen4.ninja
verify return:1
---

user@laptop:~$ https --json --proxy ``http://84.17.47.148:9002`` ``https://thisismyip.com/``
HTTP/1.1 200 OK
Alt-Svc: h3=":443"; ma=86400
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=UTF-8
Date: Thu, 06 Nov 2025 12:16:18 GMT
Server: nginx
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: PHP/8.4.14

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

My actual code:

use std::time::Duration;
use std::path::Path;
use dotenvy::dotenv;
use reqwest::{retry, Client, Proxy};
use log::{info, debug, trace, error};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    dotenvy::from_path(Path::new("src/.env"))?;
    //dotenv().expect(".env file not found!"); //Set environment variables from .env file
    env_logger::init(); //Initialize logging with env_logger

    make_request().await?;
    Ok(())
}

async fn make_request() -> Result<(), Box<dyn std::error::Error>> {
    // Basic HTTP proxy
    let proxy_url_http = "http://188.114.96.138:80";
    let proxy_url_https = "http://84.17.47.148:9002";
    let proxy_url_socks5 = "socks5://37.221.193.221:19138";

    let http_proxy = Proxy::all(proxy_url_http)?;
    let https_proxy = Proxy::all(proxy_url_https)?;
    let socks5_proxy = Proxy::all(proxy_url_socks5)?;

    let target_url_http = "http://httpforever.com/";
    let target_url_https = "https://thisismyip.com/";

    {
        //Request without proxy, to both URLs
        debug!("##### Request without proxy, to both URLs #####");

        let client = Client::builder()
            //.connection_verbose(true)
            .build()?;

        let mut resp_http = client.get(target_url_http).send().await?;
        let mut resp_https = client.get(target_url_https).send().await?;

        let mut text_http = resp_http.text().await.unwrap();
        let mut text_https = resp_https.text().await.unwrap();

        String::split_off(&mut text_http, 50);
        String::split_off(&mut text_https, 50);

        debug!("RESPONSE resp_http Chunk: [{}]", text_http);
        debug!("RESPONSE resp_https Chunk: [{}]", text_https);
    }
    {
        //Request via http proxy, to both URLs
        debug!("##### Request via HTTP proxy, to both URLs #####");

        let client = Client::builder()
            //.connection_verbose(true)
            .connect_timeout(Duration::from_secs(10))  // Time to establish connection
            .timeout(Duration::from_secs(60))          // Total request timeout
            .read_timeout(Duration::from_secs(30))     // Time to read response data
            .proxy(http_proxy)
            .build()?;

        let mut resp_http = client.get(target_url_http).send().await;

        match resp_http {
            Ok(resp) => {
                let mut text_http = resp.text().await.unwrap();
                let _ = String::split_off(&mut text_http, 50);
                debug!("RESPONSE resp_http Chunk (via http proxy): [{}]", text_http);
            },
            Err(e) => {
                error!("resp_http (via http proxy). CheckProxyError: {}.",e);
            },
        }

        let mut resp_https = client.get(target_url_https).send().await;

        match resp_https {
            Ok(resp) => {
                let mut text_https = resp.text().await.unwrap();
                let _ = String::split_off(&mut text_https, 50);
                debug!("RESPONSE resp_https Chunk (via http proxy): [{}]", text_https);
            },
            Err(e) => {
                error!("resp_https (via http proxy). CheckProxyError: {}.",e);
            },
        }
    }
    {
        //Request via https proxy, to both URLs
        debug!("##### Request via HTTPS proxy, to both URLs #####");
       
        let client = Client::builder()
            //.connection_verbose(true)
            .connect_timeout(Duration::from_secs(10))  // Time to establish connection
            .timeout(Duration::from_secs(60))          // Total request timeout
            .read_timeout(Duration::from_secs(30))     // Time to read response data
            .proxy(https_proxy)
            .build()?;

        let mut resp_http = client.get(target_url_http).send().await;
        match resp_http {
            Ok(resp)  => {
                let mut text_http = resp.text().await.unwrap();
                let _ = String::split_off(&mut text_http, 50);
                debug!("RESPONSE resp_http Chunk (via HTTPS proxy): [{}]", text_http);
            },
            Err(e) => {
                error!("resp_http (via HTTPS proxy). CheckProxyError: {}.",e);
            },
        }

        let mut resp_https = client.get(target_url_https).send().await;
        match resp_https {
            Ok(resp)  => {
                let mut text_https = resp.text().await.unwrap();
                let _ = String::split_off(&mut text_https, 50);
                debug!("RESPONSE resp_https Chunk (via HTTPS proxy): [{}]", text_https);
            },
            Err(e) => {
                error!("resp_https (via HTTPS proxy). CheckProxyError: {}.",e);
            },
        }
    }
    {
        //Request via socks5 proxy, to both URLs
        debug!("##### Request via socks5 proxy, to both URLs #####");

        let client = Client::builder()
            //.connection_verbose(true)
            .connect_timeout(Duration::from_secs(10))  // Time to establish connection
            .timeout(Duration::from_secs(60))          // Total request timeout
            .read_timeout(Duration::from_secs(30))     // Time to read response data
            .proxy(socks5_proxy)
            .build()?;

        let mut resp_http = client.get(target_url_http).send().await;
        match resp_http {
            Ok(resp)  => {
                let mut text_http = resp.text().await.unwrap();
                let _ = String::split_off(&mut text_http, 50);
                debug!("RESPONSE resp_http Chunk (via socks5 proxy): [{}]", text_http);
            },
            Err(e) => {
                error!("resp_http (via socks5 proxy). CheckProxyError: {}.",e);
            },
        }

        let mut resp_https = client.get(target_url_https).send().await;
        match resp_https {
            Ok(resp)  => {
                let mut text_https = resp.text().await.unwrap();
                let _ = String::split_off(&mut text_https, 50);
                debug!("RESPONSE resp_https Chunk (via socks5 proxy): [{}]", text_https);
            },
            Err(e) => {
                error!("resp_https (via socks5 proxy). CheckProxyError: {}.",e);
            },
        }
    }
    Ok(())
}

If this is an HTTPS proxy, shouldn't the scheme be "https://"?

As I understand it, HTTP proxies that support the “HTTP CONNECT” method can establish a TCP tunnel to a destination server, thus enabling a client behind an HTTP proxy to access websites using SSL or TLS (i.e. HTTPS).
So, I don’t think that “http://“ is a problem. The HTTP proxies I am using do support HTTP CONNECT.
Or am I missing something?

I'm not very familiar with proxies, but I'd expect "HTTPS proxy" to mean "a proxy that the client connects to using HTTPS." That's what you're doing in the openssl s_client example, for one thing.

See also: https://stackoverflow.com/questions/45874515/what-is-https-proxy

Actually, that proxy is from an “HTTPS” proxy list that has only this kind of http:// proxies. Actually all HTTPS proxy lists i found were like that e. here.

As I understand it (:thinking:) both types of proxy exist. They have the same goal: to allow a client to connect to a destination using HTTPS. The difference is mostly at the very beginning: a client’s first connection to the “HTTPS” server is already encrypted however the first connection to the “HTTP” server with “HTTP CONNECT support” is not (however subsequent communication with the destination server is).