# How to Use webtor-rs in a Web Browser: A Complete WASM Guide

> Learn how to use webtor-rs in your web browser. This guide shows you how to compile webtor-rs to WebAssembly and run a Tor client for anonymous HTTP requests directly in your browser.

- Repository: [igor53627/webtor-rs](https://github.com/igor53627/webtor-rs)
- Tags: how-to-guide
- Published: 2026-03-04

---

**webtor-rs compiles to WebAssembly and runs a full Tor client in any modern browser, exposing a JavaScript API for anonymous HTTP requests through Snowflake bridges without requiring browser plugins.**

webtor-rs is a pure-Rust Tor client maintained by igor53627 that targets WebAssembly (WASM) to bring censorship-resistant browsing directly to web pages. According to the source code in [`webtor-wasm/src/lib.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor-wasm/src/lib.rs), the library uses `wasm-bindgen` to expose Rust structs like `TorClient` and `TorClientOptions` to JavaScript, while the underlying implementation in [`webtor/src/client.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/client.rs) handles circuit creation using the official Arti `tor-proto` stack.

## Building and Loading the WASM Module

The workflow begins with `wasm-pack`, which generates a JavaScript glue file ([`webtor_wasm.js`](https://github.com/igor53627/webtor-rs/blob/main/webtor_wasm.js)) and the binary (`webtor_wasm_bg.wasm`). The module exports an initialization function that verifies secure cryptographically-secure pseudorandom number generator (CSPRNG) availability and installs a tracing layer that forwards Rust logs to the browser console.

In your HTML or JavaScript module, load the package dynamically and bootstrap the runtime:

```javascript
import * as wasm from './pkg/webtor_wasm.js';

// Fetch and instantiate the .wasm binary
await wasm.default();

// Verify CSPRNG and set up logging
await wasm.init();

```

The `init()` function in [`webtor-wasm/src/lib.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor-wasm/src/lib.rs) performs a mandatory security check that aborts early in insecure environments, ensuring the Tor client has access to `SubtleCrypto` for key generation.

## Configuring TorClientOptions for Browser Use

The bindings expose a JavaScript class that mirrors the Rust `TorClientOptions` builder pattern defined in [`webtor/src/config.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/config.rs). In browser environments, only the **Snowflake** transport is supported, so you must provide a Snowflake relay URL during construction.

```javascript
const options = new wasm.TorClientOptions('wss://snowflake.torproject.net/')
    .withCreateCircuitEarly(true)      // Start handshake immediately
    .withConnectionTimeout(60_000)     // 60 seconds in milliseconds
    .withCircuitTimeout(180_000);      // 3 minutes

```

The `BridgeType` enum in [`webtor/src/config.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/config.rs) determines transport selection; WASM builds restrict this to `Snowflake` (WebSocket) and `SnowflakeWebRtc`. All builder methods map 1-to-1 to the underlying Rust implementation, allowing fine-tuning of timeouts and early circuit creation.

## Instantiating the Tor Client

Create the client by passing the options to the `TorClient` constructor, which returns a JavaScript `Promise` that resolves to a wrapper holding an `Arc` pointer to the native `webtor::TorClient`:

```javascript
const client = await new wasm.TorClient(options);
console.log('Tor client ready');

```

The constructor logic in [`webtor-wasm/src/lib.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor-wasm/src/lib.rs) (lines 24-44) handles the conversion between JavaScript objects and Rust structures, managing the reference-counted pointer to ensure safe memory access across the WASM boundary.

## Establishing Tor Circuits

Before making requests, you must wait for the client to bootstrap and build a 3-hop circuit. The `waitForCircuit()` method initiates a connection through the Snowflake WebSocket, then layers **Turbo** framing, **KCP** reliability, and **SMUX** multiplexing before establishing the TLS channel:

```javascript
await client.waitForCircuit();
console.log('Circuit established');

```

The implementation in [`webtor/src/client.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/client.rs) uses the `wait_for_circuit` method to create a `tor_proto::Channel` and manage circuit state through the `CircuitManager`. This process includes loading an embedded compressed consensus snapshot from `webtor/src/cached/consensus.txt.br` to avoid CORS issues during directory fetches.

## Making Anonymous HTTP Requests

Once the circuit is active, the wrapper provides JavaScript-friendly methods for HTTP traffic. All requests route through the established Tor circuit and return a `JsHttpResponse` object containing `status`, `headers`, `body` (as `Uint8Array`), and `url`.

**Simple GET request:**

```javascript
const resp = await client.fetch('https://api64.ipify.org?format=json');
const body = await resp.text();
console.log('Exit IP:', JSON.parse(body).ip);

```

**POST with JSON:**

```javascript
const resp = await client.postJson('https://httpbin.org/post', { foo: 'bar' });
const json = await resp.json();

```

**Generic request with custom headers and timeout:**

```javascript
const resp = await client.request(
    'POST',
    'https://httpbin.org/post',
    { 'Content-Type': 'application/json', 'X-Custom-Header': 'value' },
    new TextEncoder().encode(JSON.stringify({ data: 'payload' })),
    30_000  // Timeout in milliseconds
);

```

The request methods in [`webtor-wasm/src/lib.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor-wasm/src/lib.rs) handle conversion between JavaScript strings/Uint8Arrays and Rust byte vectors, while the underlying client in [`webtor/src/client.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/client.rs) manages stream multiplexing across the Tor circuit.

## Error Handling and Lifecycle Management

All Rust `TorError` types convert to structured `JsTorError` objects containing:
- `code`: Stable identifier (e.g., `"CIRCUIT_CREATION"`)
- `kind`: Category (`"network"`, `"timeout"`, `"configuration"`)
- `message`: Human-readable description
- `retryable`: Boolean indicating if retry might succeed

```javascript
try {
    const resp = await client.fetch('https://example.invalid');
} catch (err) {
    console.error('Tor error:', err.code, err.message);
    if (err.retryable) {
        // Implement retry logic
    }
}

```

When finished, explicitly close the client to shut down channels and clean up resources:

```javascript
await client.close();

```

You can also abort pending operations immediately using `client.abort()`.

## React Integration Example

For React applications, dynamically import the WASM package in a component effect as demonstrated in [`example/src/App.jsx`](https://github.com/igor53627/webtor-rs/blob/main/example/src/App.jsx):

```jsx
const loadWasm = async () => {
    const wasm = await import('../pkg/webtor_wasm.js');
    await wasm.default();
    wasm.init();
    return wasm;
};

// Inside component
const client = await new wasm.TorClient(options);
await client.waitForCircuit();
const response = await client.fetch('https://check.torproject.org');
// ...
await client.close();

```

## Summary

- **Load the module** using dynamic `import()` and call `wasm.default()` followed by `wasm.init()` to initialize the CSPRNG and logging.
- **Configure options** with `TorClientOptions`, specifying a Snowflake bridge URL as the transport (the only supported method in browsers).
- **Instantiate the client** with `new wasm.TorClient(options)` and await `waitForCircuit()` to complete the 3-hop Tor handshake.
- **Perform requests** using `fetch()`, `post()`, `postJson()`, or the generic `request()` method for full control over HTTP semantics.
- **Handle errors** by catching `JsTorError` objects that expose structured metadata for programmatic retry decisions.
- **Clean up** by calling `client.close()` to release the `Arc`-held native client and terminate WebSocket connections.

## Frequently Asked Questions

### What browsers support webtor-rs?

Any browser supporting WebAssembly and the WebCrypto API (specifically `SubtleCrypto`) can run webtor-rs, including Chrome, Firefox, Safari, and Edge. The initialization function in [`webtor-wasm/src/lib.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor-wasm/src/lib.rs) explicitly checks for secure randomness availability and aborts if the environment lacks required cryptographic primitives.

### Which transport protocols work in the browser?

Only **Snowflake** bridges function in WASM builds. The `BridgeType` enum in [`webtor/src/config.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/config.rs) restricts browser clients to `Snowflake` (WebSocket-based) or `SnowflakeWebRtc` transports, routing through Turbo framing, KCP reliability, and SMUX multiplexing before reaching the Tor network. Traditional TCP or obfs4 bridges are unavailable due to browser sandboxing constraints.

### How does webtor-rs handle the Tor consensus without CORS issues?

The client embeds a compressed consensus snapshot directly in the WASM binary at `webtor/src/cached/consensus.txt.br`. During bootstrap, the `Directory` implementation loads this embedded data before attempting network fetches, eliminating CORS complications since the initial relay discovery requires no external HTTP requests.

### Can I use webtor-rs with frontend frameworks like React or Vue?

Yes. The repository includes a complete React example in [`example/src/App.jsx`](https://github.com/igor53627/webtor-rs/blob/main/example/src/App.jsx) showing dynamic module loading, state management for the Tor client, and proper cleanup in component unmount effects. The WASM package exports standard ES modules compatible with webpack, Vite, and other modern bundlers.