How webtor-rs Enables Anonymous HTTP/HTTPS Connections Over Tor

webtor-rs enables anonymous HTTP/HTTPS connections by constructing Tor circuits in pure Rust, applying stream isolation policies to prevent correlation attacks, and wrapping TCP streams with TLS—using rustls for native targets and subtle-tls for WebAssembly.

The igor53627/webtor-rs repository implements a complete Tor client in Rust that compiles to both native binaries and WebAssembly. Unlike proxy-based solutions, this library embeds the full circuit construction logic, allowing applications to make HTTP requests without revealing the client IP address or linking separate requests to the same identity.

How webtor-rs Routes Traffic Through Tor

Anonymous connections in webtor-rs follow a three-stage pipeline: circuit construction with isolation, stream creation, and HTTP/TLS handling. Each stage is designed to prevent IP leakage and correlation attacks while supporting both desktop and browser environments.

Circuit Construction and Stream Isolation

The CircuitManager in webtor/src/client.rs assembles Tor circuits consisting of a guard, middle, and exit relay. When a new HTTP request is initiated, the client derives an IsolationKey from the target URL via IsolationKey::from_url in webtor/src/isolation.rs.

This isolation key ensures that distinct domains never share the same circuit, preventing exit nodes from correlating traffic across different destinations. The policy defaults to StreamIsolationPolicy::PerDomain, though applications can configure alternative strategies for session-based or request-based isolation.

Opening Anonymous Streams

Once a circuit is selected, the client opens a raw TCP stream to the destination host and port through the exit relay. The Circuit::begin_stream method in webtor/src/circuit.rs handles this negotiation, establishing the end-to-end connection without exposing the client's actual IP address.

This stream serves as the underlying transport for the HTTP payload, ensuring that all network-level communication appears to originate from the Tor exit node rather than the originating device.

HTTP Request Handling and TLS Encryption

The TorHttpClient in webtor/src/http.rs orchestrates the final request phase. For each request, it constructs the HTTP byte buffer using HttpRequest::build_request, then determines whether TLS is required based on the URL scheme.

For native targets, the stream is wrapped using wrap_with_tls in webtor/src/tls.rs, which utilizes rustls for TLS 1.2/1.3 encryption. For WebAssembly builds, the client automatically switches to subtle-tls (located in the subtle-tls crate), which implements TLS using the WebCrypto API available in browsers.

After establishing the secure tunnel, the client writes the HTTP request bytes, reads the response, and handles chunked transfer-encoding and Content-Length validation before returning the parsed HttpResponse.

Anonymous HTTP/HTTPS Request Flow in Code

The following example demonstrates how to configure a TorHttpClient with per-domain isolation and execute anonymous GET and POST requests:

use std::sync::Arc;
use tokio::sync::RwLock;
use webtor::{
    client::TorClient,
    http::{TorHttpClient, HttpRequest},
    relay::RelayManager,
    circuit::CircuitManager,
    isolation::StreamIsolationPolicy,
};

#[tokio::main]
async fn main() -> webtor::Result<()> {
    // Initialize relay list (normally populated from Tor consensus)
    let relays = vec![
        webtor::relay::Relay::new(
            "fingerprint1".into(),
            "guard1".into(),
            "1.1.1.1".into(),
            9001,
            vec!["Fast".into(), "Guard".into()],
            "0000...".into(),
        ),
        // ... middle and exit relays
    ];

    // Build core Tor client components
    let relay_manager = RelayManager::new(relays);
    let circuit_manager = Arc::new(RwLock::new(CircuitManager::new(
        Arc::new(RwLock::new(relay_manager)),
        Arc::new(RwLock::new(None)),
    )));
    let tor_client = TorClient::new(circuit_manager.clone());

    // Create HTTP client with per-domain stream isolation
    let http = TorHttpClient::new(circuit_manager, StreamIsolationPolicy::PerDomain);

    // Anonymous GET request
    let resp = http.get("https://httpbin.org/ip").await?;
    println!("Status: {}", resp.status);
    println!("Body: {}", resp.text()?);

    // Anonymous POST request with JSON payload
    let json = br#"{"msg":"hello"}"#.to_vec();
    let resp = http.post("https://httpbin.org/post", json).await?;
    println!("POST response: {}", resp.text()?);

    Ok(())
}

When compiled for WebAssembly, the same code automatically uses the subtle-tls implementation for TLS handshakes via the WebCrypto API, requiring no API changes.

Key Implementation Files

The anonymous connection capability is distributed across these core modules:

Summary

  • webtor-rs implements a complete Tor client in Rust that supports both native and WebAssembly targets for anonymous HTTP/HTTPS connections.
  • Stream isolation via IsolationKey::from_url ensures distinct domains use separate Tor circuits, preventing traffic correlation.
  • Circuit construction in CircuitManager and Circuit::begin_stream routes TCP streams through guard, middle, and exit relays without exposing the client IP.
  • TLS abstraction automatically selects rustls for native builds and subtle-tls (WebCrypto) for WASM, enabling secure HTTPS requests in browsers without native code.
  • All traffic exits through Tor exit nodes, ensuring the destination server sees only the exit relay's IP address.

Frequently Asked Questions

How does webtor-rs prevent different websites from correlating my requests?

webtor-rs implements stream isolation through the IsolationKey struct in webtor/src/isolation.rs. By default, it uses StreamIsolationPolicy::PerDomain, which derives a unique isolation key from each URL's host component. The CircuitManager ensures that requests with different isolation keys are routed through separate Tor circuits (distinct guard, middle, and exit relays), preventing exit nodes from linking traffic across different destinations.

Can I use webtor-rs in a web browser?

Yes. webtor-rs compiles to WebAssembly using the wasm32-unknown-unknown target. When running in a browser, the library automatically switches from the native rustls TLS implementation to subtle-tls, which performs TLS handshakes using the WebCrypto API. This allows the Tor client to establish secure HTTPS connections within the browser sandbox without requiring native code or browser extensions.

What happens if a TLS 1.3 connection fails in webtor-rs?

The TorHttpClient in webtor/src/http.rs includes automatic fallback logic for TLS handshake failures. If a TLS 1.3 connection attempt fails (common with older servers or certain exit relays), the client automatically retries the connection using TLS 1.2. This fallback ensures maximum connectivity while maintaining the anonymity guarantees of the underlying Tor circuit, as the retry occurs over the same isolated circuit without exposing the client identity.

Does webtor-rs support HTTP/2 or only HTTP/1.1?

Based on the current implementation in webtor/src/http.rs, webtor-rs primarily targets HTTP/1.1 through its HttpRequest::build_request method and manual response parsing (handling chunked transfer-encoding and Content-Length). The library focuses on compatibility and anonymity rather than advanced protocol features. HTTP/2 support would require additional implementation of the HTTP/2 framing layer atop the existing Tor stream infrastructure.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →