# Running a Tor Bridge on Linux: obfs4 and WebTunnel Guide

> One post from jmrp.io, published as its own document. Index: https://jmrp.io/llms-full.txt

Canonical: https://jmrp.io/blog/009-running-tor-bridge/
Language: en
Alternate: https://jmrp.io/es/blog/009-running-tor-bridge/index.md
License: https://creativecommons.org/licenses/by/4.0/
Type: TechArticle
Published: 2026-03-30
Updated: 2026-09-22
Instructions re-tested: 2026-08-22 · Tor 0.4.9.11 · obfs4proxy 0.0.14 · Debian 13
Author: José Manuel Requena Plens
Summary: Guide to running obfs4 and WebTunnel Tor bridges on Linux with Nginx camouflage, firewall hardening, Prometheus monitoring, and CrowdSec.
Tags: Privacy, Linux, Security, Nginx
Topics: Tor (Q202044), Pluggable transport (Q97466190), Internet censorship circumvention (Q839180), Onion routing (Q392095), Deep packet inspection (Q734252), Nginx (Q306144)
Build-Date: 2026-09-27

Questions answered:

**What is a Tor bridge and why are bridges so important?**

A bridge is a hidden entry point into the Tor network that is not listed in the public Tor directory, so it helps users in censored countries reach Tor even when known relays are blocked. As of 2026 the network has roughly 10,000 relays but only about 2,600 bridges, making them the most critical and scarce resource for censorship circumvention.

**What is the difference between obfs4 and WebTunnel?**

obfs4 makes Tor traffic look like random bytes to resist DPI fingerprinting and is easy to set up, while WebTunnel disguises traffic as ordinary HTTPS/WebSocket browsing, making it nearly impossible to block but requiring a web server and domain. Running both on one server provides two different circumvention methods at once.

**Why must ORPort use 0.0.0.0 instead of 127.0.0.1 behind NAT?**

Tor runs a self-reachability test that verifies the ORPort is accessible from the internet, and binding to localhost (127.0.0.1) makes that test fail. Behind a router you must use ORPort 0.0.0.0:9001 (or your LAN IP) and forward port 9001 from the router.

**Which ports do I need to forward for a Tor bridge?**

Forward TCP 9001 (ORPort, always required), TCP 4443 (obfs4, if running obfs4), and TCP 443 (HTTPS for WebTunnel via Nginx, if running WebTunnel). IPv6 addresses are globally routable, so they need firewall allow rules instead of port forwarding.

**Why does curling the WebTunnel secret path return a 502 error?**

A 502 Bad Gateway when curling the secret path is expected behavior, because WebTunnel requires a proper WebSocket/WebTunnel protocol handshake that a plain HTTP GET cannot perform. If Tor clients can connect, everything is working correctly.

**How long until my bridge gets relay flags and client connections?**

Flags appear gradually: Running and Valid take about 1 hour, Stable needs about 7 days of continuous uptime, and Guard about 8 days. BridgeDB distributes new bridges over 24–48 hours, so it is normal to wait up to a week before seeing your first client connections.


Steps (Run an obfs4 + WebTunnel Tor bridge on Linux):
1. Install Tor from the official repository
2. Compile and install the WebTunnel server
3. Configure the Tor bridge in torrc
4. Configure Nginx for WebTunnel camouflage
5. Configure port forwarding and the firewall
6. Allow obfs4proxy in AppArmor
7. Start the bridge and verify it works
8. Set up Prometheus and Grafana monitoring
9. Integrate CrowdSec without blocking Tor clients

---

In 2026, over **2.5 million people** a day reach the free and open internet through the [Tor network](https://www.torproject.org/) by connecting straight to a relay — [Tor Metrics puts that daily estimate above three million for most of the year](https://metrics.torproject.org/userstats-relay-country.html), and it counts only direct connections: everyone arriving through a bridge, which is what this guide is about, is counted separately and is not in that figure. Journalists in authoritarian regimes, activists organizing protests, whistleblowers exposing corruption, and ordinary citizens protecting their privacy — all of them depend on a network that is entirely **volunteer-operated**.

The problem? There are only about **2,600 bridges** serving those millions of users. Bridges are the most critical and scarce resource in the Tor ecosystem — they're the first point of contact for users in censored countries, and every new bridge directly helps someone access information freely.

This guide will teach you everything you need to know about the Tor network, onion routing, and how to set up a **production-ready obfs4 + WebTunnel bridge** on Linux — complete with Nginx camouflage, firewall hardening, Prometheus monitoring, and CrowdSec integration.

## TL;DR — one bridge, two pluggable transports

- **One bridge can run obfs4 and WebTunnel at the same time**, which is worth doing because they fail in different places: obfs4 randomizes the traffic so it matches no protocol, while WebTunnel hides inside ordinary HTTPS on port 443 and survives censors that block "unclassifiable" traffic outright.
- **Nginx is what makes WebTunnel credible.** It fronts the bridge on 443 and serves a real website on every path except the secret one, so a censor probing the host sees a normal site rather than an endpoint that answers oddly.
- **The consensus flags take days, not minutes.** Running and Valid arrive within the hour; Stable and Guard took about a week on this deployment. They are scored from weighted mean time between failures and, for Guard, bandwidth and familiarity too — not a plain uptime counter — so a bridge restarted every other day keeps resetting the evidence they are scored on, and may not reach them at all.
- **Behind NAT, forward TCP 9001, 4443 and 443** — ORPort, obfs4 and WebTunnel respectively — and open them on the host firewall; the ORPort self-test fails silently if the router does not.
- **Tell your IDS to leave Tor clients alone.** CrowdSec sees many connections from many addresses and bans legitimate users unless the bridge ports are excluded.
- **Everything runs under systemd**, so the stack comes back after a reboot without intervention — which is exactly what the uptime-based flags require.

---

## The Tor Network: A Primer

### What is Tor?

**[Tor](https://en.wikipedia.org/wiki/Tor_%28network%29)** (The Onion Router) is a decentralized, volunteer-operated network designed to protect users' privacy and resist censorship. When you use Tor, your internet traffic is routed through a series of encrypted relays, making it extremely difficult for anyone — your ISP, government, or a malicious actor — to trace your activity back to you.

But Tor isn't just a privacy tool. It's an essential piece of **human rights infrastructure**. In countries like China, Iran, Russia, and Myanmar, where governments actively block access to information, Tor is often the only way for citizens to reach the uncensored internet.

**Info — A Common Misconception**

Only about **1.5%** of Tor traffic goes to `.onion` sites (the so-called "dark web"). The vast majority — over **98%** — is ordinary people browsing the regular internet privately. Tor is primarily a privacy and anti-censorship tool, not a gateway to illegal content.

### A Brief History

The [origins of Tor](https://www.onion-router.net/History.html) trace back to the 1990s at the **U.S. Naval Research Laboratory** (NRL), where researchers [Paul Syverson](https://en.wikipedia.org/wiki/Paul_Syverson), David Goldschlag, and Michael Reed developed the concept of [onion routing](https://en.wikipedia.org/wiki/Onion_routing) — a technique for anonymous communication over a computer network. You can read more about [Tor's full history](https://www.torproject.org/about/history/) on the official site.

- **1995 — Onion Routing Invented**: Syverson, Goldschlag, and Reed at the U.S. Naval Research Laboratory develop the concept of layered encryption for anonymous communication.
- **2002 — Tor Project Launched**: Roger Dingledine and Nick Mathewson (MIT graduates) join Syverson to build 'The Onion Router' — Tor. The code is released as open-source with about a dozen volunteer nodes.
- **2004 — EFF Begins Funding**: The Electronic Frontier Foundation recognizes Tor's importance for digital rights and provides critical early funding. NRL releases the code to the public domain.
- **2006 — Tor Project, Inc. Founded**: The Tor Project becomes a 501(c)(3) nonprofit organization based in Massachusetts, ensuring long-term development and governance.
- **2007 — Bridges Introduced**: Secret relay addresses (bridges) are developed to help users bypass firewalls in censored countries where the Tor directory is blocked.
- **2008 — Tor Browser Development Begins**: A dedicated browser that packages Tor into an easy-to-use application, dramatically lowering the barrier to entry for non-technical users.
- **2011 — Arab Spring**: Tor plays a critical role as activists in Tunisia, Egypt, Libya, and Syria use it to organize protests and communicate securely despite government censorship.
- **2013 — Snowden Revelations**: Edward Snowden's disclosures about global mass surveillance programs drive a massive surge in Tor adoption worldwide.
- **2023 — WebTunnel Released**: A new pluggable transport that disguises Tor traffic as ordinary HTTPS, making it nearly impossible to block without collateral damage.
- **2026 — Today**: ~10,000 relays, ~2,600 bridges, and 2.5+ million daily users across 200+ countries. The network is entirely volunteer-operated.

### How does onion routing work?

[Onion routing](https://en.wikipedia.org/wiki/Onion_routing) derives its name from the layers of an onion — your data is wrapped in **multiple layers of encryption**, and each relay in the circuit "peels off" one layer to reveal the next destination. No single relay ever knows both the origin and the destination of the traffic.

**Onion routing: three layers of encryption through three relays**

```mermaid
sequenceDiagram
    participant User
    participant Guard as Guard Relay
    participant Middle as Middle Relay
    participant Exit as Exit Relay
    participant Web as Destination

    Note over User: Encrypts with 3 layers:<br/>Layer 3 (Guard key)<br/>Layer 2 (Middle key)<br/>Layer 1 (Exit key)

    User->>Guard: 🧅🧅🧅 [3 layers encrypted]
    Note right of Guard: Peels Layer 3<br/>Knows: User IP<br/>Doesn't know: Destination
    Guard->>Middle: 🧅🧅 [2 layers encrypted]
    Note right of Middle: Peels Layer 2<br/>Knows: Nothing useful<br/>(only prev & next hop)
    Middle->>Exit: 🧅 [1 layer encrypted]
    Note right of Exit: Peels Layer 1<br/>Knows: Destination<br/>Doesn't know: User IP
    Exit->>Web: 📄 [Plaintext or HTTPS]
```

Here's what makes this secure:

**What Each Relay Knows**

| Relay | Knows | Doesn't Know |
| --- | --- | --- |
| **Guard** (Entry) | Your real IP address | What websites you're visiting |
| **Middle** | Previous and next relay only | Neither your IP nor your destination |
| **Exit** | The destination website | Your real IP address |

Key technical details of Tor's [circuit construction](https://spec.torproject.org/tor-spec/creating-circuits.html) (see also the [system overview](https://tpo.pages.torproject.net/core/torspec/tor-spec/system-overview.html) in the Tor specification):

- **Circuit path selection**: The client randomly selects relays, avoiding two relays in the same `/16` subnet or country for diversity.
- **Key exchange**: Each hop uses an **ntor handshake** (based on Curve25519) to establish a shared secret, providing perfect forward secrecy.
- **Fixed-size cells**: All data inside Tor is transmitted in **512-byte cells**, preventing traffic analysis based on packet size.
- **Circuit rotation**: Circuits are rotated approximately every **10 minutes** to limit the window for traffic correlation attacks.
- **Directory authorities**: Nine trusted servers maintain consensus about the network state, relay keys, and flags.

---

## Which kind of Tor node can you volunteer to run?

The Tor network is made up of different types of volunteer-operated nodes, each serving a distinct purpose:

**Tor Volunteer Roles**

| Role | What It Does | Risk Level | Impact |
| --- | --- | --- | --- |
| **Guard Relay** | First hop in the circuit. Sees user IP but not destination. | Low | High — provides bandwidth |
| **Middle Relay** | Intermediate hop. Sees nothing useful. | Very Low | Medium — adds bandwidth and path diversity |
| **Exit Relay** | Final hop. Connects to the destination on behalf of the user. | High | Very High — the most needed and scarce resource |
| **Bridge** | Hidden entry point for users in censored countries. Not listed in the public Tor directory. | Low | **Critical** — directly enables censorship circumvention |
| **Snowflake Proxy** | Ephemeral WebRTC proxy that helps censored users reach the Tor network. | Very Low | Medium — easy to run, even in a browser tab |
| **Directory Authority** | Trusted server that maintains network consensus. | N/A | Critical — only 9 exist, run by the Tor Project |

**Tip — Why Bridges Matter Most Right Now**

As of 2026, the [Tor network has roughly **10,000 relays** but only about **2,600 bridges**](https://metrics.torproject.org/networksize.html). Bridges are particularly valuable because they serve users who are **actively blocked** from accessing Tor — people in countries like China, Iran, and Russia where the government blocks connections to known Tor relays. Every new bridge is a **lifeline** for someone under censorship.

### What are pluggable transports?

Censors don't just block Tor by IP — they use **Deep Packet Inspection (DPI)** to identify and block the Tor protocol itself. [Pluggable transports](https://arxiv.org/pdf/2309.14856) solve this by disguising Tor traffic as something else entirely.

**Pluggable Transport Comparison**

| Transport | Disguise Method | Censorship Resistance | Speed | Deployment |
| --- | --- | --- | --- | --- |
| **[obfs4](https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/lyrebird)** | Makes traffic look like random bytes | High — resists DPI fingerprinting | Fast | Widely deployed, easy to set up |
| **[WebTunnel](https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/webtunnel)** | Mimics HTTPS/WebSocket traffic | Very High — looks like normal web browsing | Fast | Newer, requires web server + domain |
| **Snowflake** | Uses WebRTC through ephemeral proxies | Very High — proxies rotate constantly | Variable | Client-side only (volunteers run browser proxies) |
| **meek** | Disguises traffic as requests to cloud services (Azure, CDN) | Extreme — blocking it means blocking cloud services | Slow | Expensive to maintain, last resort |

**Which should you run? Here's how to decide:**

**If: I want maximum impact with minimal complexity**

Run an **obfs4 bridge**. It's the most widely deployed pluggable transport, easy to configure, and serves the largest number of users. This is where the Tor Project needs the most help.

**If: I already have a web server and domain name**

Run **both obfs4 and WebTunnel**. WebTunnel disguises Tor traffic as normal HTTPS browsing, making it nearly impossible to block. When combined with obfs4, you provide two different circumvention methods from a single server.

**If: I don't have a server, but I want to help right now**

Install the [Snowflake browser extension](https://snowflake.torproject.org/) — it takes 30 seconds and turns your browser into a temporary proxy for censored users. Zero maintenance required.

**If: I want to contribute maximum bandwidth to the network**

Run a **middle relay** or (if you're comfortable with the legal implications) an **exit relay**. These provide raw bandwidth capacity for the entire network. Consider running them on a VPS with an ISP that explicitly allows Tor traffic.

In this guide, we'll set up **both obfs4 and WebTunnel** on the same server — maximizing your impact with a single machine.

---

## Prerequisites

**Prerequisites**

### Hardware Requirements

- **CPU**: Any modern x86_64 processor (ARM also works)
- **RAM**: 1 GB minimum (what WebTunnel requires), 4 GB recommended if you expect real traffic
- **Storage**: 5 GB minimum (for Tor data, logs, and keys)
- **Network**: At least **10 Mbps** upload bandwidth (more is better)
- **Uptime**: The server should run 24/7 — Tor penalizes unstable relays

### Software Requirements

- **Linux** distribution (this guide uses Debian 12/13 or Ubuntu 22.04+)
- **Root access** (or sudo privileges)
- **Nginx** (for WebTunnel camouflage — not needed for obfs4 only)
- **A domain name** with DNS pointing to your server (for WebTunnel only)
- **TLS certificate** (Let's Encrypt is free and perfect for this)

### Network Requirements

- Ability to **forward ports** from your router (or use a VPS with direct public IP)
- Ports **9001** (ORPort), **4443** (obfs4) and **443** (Nginx HTTPS, if you serve WebTunnel) accessible from the internet
- Port **443** (HTTPS) if running WebTunnel

---

## Step 1: Install Tor from the Official Repository

**Warning — Always Use the Official Tor Repository**

Distribution packages (e.g., `apt install tor`) are often outdated by months or even years. The Tor Project maintains its own [Debian/Ubuntu repository](https://community.torproject.org/relay/setup/bridge/) with the latest stable releases, security patches, and signing keys. Always use it.

**Install Tor and Dependencies**

1. **Install prerequisites**

   **Install prerequisite packages**

   ```bash
   sudo apt update
   sudo apt install -y apt-transport-https gpg curl
   ```

2. **Add the Tor Project GPG key**

   **Download and install Tor Project GPG key**

   ```bash
   curl -fsSL https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc \
     | sudo gpg --dearmor -o /usr/share/keyrings/tor-archive-keyring.gpg
   ```

3. **Add the Tor repository**

   Replace `CODENAME` with your distribution's codename (e.g., `bookworm`, `trixie`, `jammy`):

   **Add Tor APT repository**

   ```bash
   CODENAME=$(lsb_release -cs)
   echo "deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] \
     https://deb.torproject.org/torproject.org $CODENAME main" \
     | sudo tee /etc/apt/sources.list.d/tor.list
   ```

4. **Install Tor and obfs4proxy**

   **Install Tor and obfs4proxy**

   ```bash
   sudo apt update
   sudo apt install -y tor deb.torproject.org-keyring obfs4proxy
   ```

   Verify the installation:

   **Verify Tor and obfs4proxy versions**

   ```bash
   tor --version
   obfs4proxy -version
   ```

   **Expected output**

   ```text
   Tor version 0.4.9.11.
   obfs4proxy-0.0.14
   ```

---

## Step 2: Install WebTunnel (Optional but Recommended)

WebTunnel is a newer pluggable transport that disguises Tor connections as regular HTTPS traffic. Unlike obfs4 (which looks like random data), WebTunnel actually uses WebSocket upgrades over HTTPS, making it **nearly indistinguishable from legitimate web browsing**. The Tor Project provides [official setup instructions](https://community.torproject.org/relay/setup/webtunnel/) and a guide for [building from source](https://community.torproject.org/relay/setup/webtunnel/source/).

**Info — Why Compile from Source?**

At the time of writing, the `webtunnel-server` binary is not yet available as a Debian package. The Tor Project distributes it as a Go source that needs to be compiled, or as a Docker image. We'll compile from source for a native installation.

**Compile WebTunnel Server**

1. **Install Go** (if not already installed)

   **Install Go programming language**

   ```bash
   sudo apt install -y golang-go
   ```

   Verify that Go 1.21+ is installed, which is what WebTunnel requires. Watch out on Debian 12:
   its `golang-go` package is Go 1.19 and does not meet that, so there you need
   `bookworm-backports` or the official tarball. Debian 13 ships 1.24 and works as is:

   **Check Go version**

   ```bash
   go version
   ```

2. **Clone and build WebTunnel**

   **Clone and build WebTunnel from source**

   ```bash
   cd /tmp
   git clone https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/webtunnel.git
   cd webtunnel/main/server
   go build -o webtunnel-server .
   sudo cp webtunnel-server /usr/local/bin/
   sudo chmod +x /usr/local/bin/webtunnel-server
   ```

3. **Verify the binary**

   **Verify WebTunnel server binary**

   ```bash
   /usr/local/bin/webtunnel-server --help 2>&1 | head -5
   ```

---

## Step 3: Configure the Tor Bridge

Now for the main configuration. The `torrc` file controls all of Tor's behavior.

**Warning — Before Editing**

Back up the original configuration before making changes:

**Backup original torrc**

```bash
sudo cp /etc/tor/torrc /etc/tor/torrc.backup
```

Replace the contents of `/etc/tor/torrc` with:

**File: `/etc/tor/torrc`**

```ini
# =============================================================
# Tor Bridge Configuration — obfs4 + WebTunnel
# =============================================================

# --- General Settings ---
SocksPort 0                        # Not a client — disable SOCKS
RunAsDaemon 0                      # Managed by systemd
DataDirectory /var/lib/tor
Log notice file /var/log/tor/notices.log
Log notice syslog

# --- Security Hardening ---
DisableDebuggerAttachment 1        # Prevent ptrace/debugger attachment

# --- Bridge Mode ---
BridgeRelay 1                      # This is a bridge, not a relay
PublishServerDescriptor bridge     # Publish to BridgeDB (not public directory)

# --- ORPort — IPv4 (NAT from router) + IPv6 (direct, global) ---
# IMPORTANT: Use 0.0.0.0 for NAT environments, not 127.0.0.1
ORPort 0.0.0.0:9001
ORPort [YOUR_IPV6_ADDRESS]:9001
ExtORPort auto

# --- Contact & Identity ---
ContactInfo your-email<at>example<dot>com
Nickname YourBridgeName

# --- obfs4 Transport ---
# Port 4443 must be forwarded from your router
ServerTransportPlugin obfs4 exec /usr/bin/obfs4proxy
ServerTransportListenAddr obfs4 0.0.0.0:4443

# --- WebTunnel Transport ---
# Listens locally; Nginx reverse-proxies HTTPS to it
ServerTransportPlugin webtunnel exec /usr/local/bin/webtunnel-server
ServerTransportListenAddr webtunnel 127.0.0.1:15000
ServerTransportOptions webtunnel url=https://YOUR-DOMAIN/YOUR-SECRET-PATH

# --- Bandwidth Limits ---
# Adjust to what your connection can sustain (example: 250 Mbps)
RelayBandwidthRate 31 MBytes       # Sustained rate
RelayBandwidthBurst 35 MBytes     # Burst allowance

# --- Memory — cap internal queue memory (OOM protection) ---
MaxMemInQueues 4096 MB

# --- Statistics ---
ConnDirectionStatistics 1          # Enable connection direction stats

# --- DoS Protection ---
DoSRefuseSingleHopClientRendezvous 1

# --- Monitoring: Prometheus Metrics ---
MetricsPort 0.0.0.0:9052
MetricsPortPolicy accept 192.168.0.0/16
MetricsPortPolicy accept 127.0.0.0/8

# --- Control Port (for Nyx monitoring tool) ---
ControlPort 9051
CookieAuthentication 1
```

Let's break down the critical sections:

**Key Configuration Options Explained**

| Key | Value | Description |
| --- | --- | --- |
| BridgeRelay 1 | Bridge mode | Tells Tor this is a bridge, not a public relay. Bridges are not listed in the public Tor directory. |
| ORPort 0.0.0.0:9001 | Onion Router Port (IPv4) | The port Tor uses for relay-to-relay communication. Use 0.0.0.0 (not 127.0.0.1) if behind NAT — Tor needs to bind on all interfaces for the self-test to pass. |
| ORPort [IPV6]:9001 | Onion Router Port (IPv6) | If your server has a global IPv6 address, add a second ORPort line with it. IPv6 is globally routable — no NAT or port forwarding needed, only firewall rules. |
| ExtORPort auto | Extended OR Port | Internal communication channel between Tor and pluggable transports. 'auto' lets Tor choose a random local port. |
| DisableDebuggerAttachment 1 | Anti-ptrace protection | Prevents debuggers from attaching to the Tor process. A simple but effective defense against local privilege escalation. |
| MaxMemInQueues 4096 MB | Memory cap for queues | Limits the memory Tor uses for internal queues. Prevents out-of-memory (OOM) kills on servers with limited RAM. Adjust based on your available memory. |
| ConnDirectionStatistics 1 | Connection direction stats | Enables collection of statistics about connection directionality, useful for monitoring and debugging via MetricsPort. |
| DoSRefuseSingleHopClientRendezvous 1 | DoS protection | Refuses rendezvous circuits from single-hop clients. These are commonly used in abuse and denial-of-service attacks against hidden services. |
| ServerTransportListenAddr obfs4 | obfs4 listening address | The port where obfs4proxy listens for incoming obfuscated connections. Must be reachable from the internet. |
| ServerTransportListenAddr webtunnel | WebTunnel listening address | Binds to 127.0.0.1 only because Nginx handles the public-facing HTTPS and reverse-proxies to this port. |
| MetricsPort | Prometheus endpoint | Exposes Tor internal metrics in Prometheus format. Restrict access with MetricsPortPolicy. |

**Error — Critical: ORPort Behind NAT**

If your server is behind a router (NAT), using `ORPort 127.0.0.1:auto` will **fail** the self-reachability test. Tor needs to verify that its ORPort is accessible from the internet, and binding to localhost prevents this. Always use `ORPort 0.0.0.0:9001` (or your specific LAN IP) and forward port 9001 from your router.

**Tip — Dual-Stack: IPv4 + IPv6**

If your ISP provides an IPv6 prefix, **strongly consider adding an IPv6 ORPort**. Dual-stack bridges can serve more users because some networks only have IPv6 connectivity. To find your server's global IPv6 address:

**Find your server global IPv6 address**

```bash
ip -6 addr show scope global | grep inet6
```

Use the address (without the `/64` prefix) in your `torrc`:

```ini
ORPort [2001:db8::1]:9001
```

Unlike IPv4, IPv6 addresses are **globally routable** — no NAT or port forwarding is required. You only need to ensure your firewall allows incoming TCP on port 9001 to that address.

### Generate a WebTunnel Secret Path

The WebTunnel transport uses a secret URL path that acts as an authentication token. Generate a random 24-character string:

**Generate a random WebTunnel secret path**

```bash
echo $(cat /dev/urandom | tr -cd 'a-zA-Z0-9' | head -c 24)
```

**Example output**

```text
7Zkw18j2NWGK9X7PiiPUQRJB
```

Use this value in two places:

1. The `torrc`: `ServerTransportOptions webtunnel url=https://your-domain.com/YOUR_SECRET`
2. The Nginx config: `location = /YOUR_SECRET`

---

## Step 4: Configure Nginx for WebTunnel Camouflage

The beauty of WebTunnel is that your server looks like a **completely normal website**. Visitors see a regular HTML page; only those who know the secret path can connect to the Tor bridge. This is **secret-path camouflage**. It is not *domain fronting*: that puts one domain in the TLS SNI and a different one in the `Host` header, whereas here there is a single domain and what hides the bridge is the path.

**Set Up Nginx Camouflage**

1. **Create a camouflage website**

   This is what visitors (and censors) see when they visit your domain:

   **Create camouflage website directory and page**

   ```bash
   sudo mkdir -p /var/www/tor-camouflage
   cat << 'EOF' | sudo tee /var/www/tor-camouflage/index.html
   <!DOCTYPE html>
   <html lang="en">
   <head>
       <meta charset="UTF-8">
       <meta name="viewport" content="width=device-width, initial-scale=1.0">
       <title>Welcome</title>
       <style>
           body { font-family: system-ui, sans-serif; max-width: 800px;
                  margin: 50px auto; padding: 20px; color: #333; }
           h1 { color: #2c3e50; }
       </style>
   </head>
   <body>
       <h1>Welcome</h1>
       <p>This is a personal project page. Nothing to see here.</p>
   </body>
   </html>
   EOF
   ```

2. **Obtain a TLS certificate** (if you don't already have one)

   **Obtain Let's Encrypt certificate**

   ```bash
   sudo apt install -y certbot python3-certbot-nginx
   sudo certbot --nginx -d your-domain.com
   ```

3. **Create the Nginx server block**

   **File: `/etc/nginx/sites-available/tor-bridge.conf`**

   ```nginx
   # Tor WebTunnel Bridge — Camouflage configuration
   # This looks like a normal HTTPS website but proxies
   # a secret path to the WebTunnel pluggable transport

   server {
       listen 443 ssl;
       listen [::]:443 ssl;
       http2 on;

       server_name your-domain.com;

       # TLS certificates
       ssl_certificate     /etc/letsencrypt/live/your-domain.com/fullchain.pem;
       ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
       ssl_protocols       TLSv1.2 TLSv1.3;
       ssl_ciphers         HIGH:!aNULL:!MD5;

       # === Normal website (camouflage) ===
       root /var/www/tor-camouflage;
       index index.html;

       location / {
           try_files $uri $uri/ =404;
       }

       # === WebTunnel reverse proxy (secret path) ===
       # Replace YOUR_SECRET_PATH with your generated secret
       location = /YOUR_SECRET_PATH {
           proxy_pass http://127.0.0.1:15000;
           proxy_http_version 1.1;

           # WebSocket upgrade headers (required for WebTunnel)
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "upgrade";

           # Standard proxy headers
           proxy_set_header Host $host;
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_set_header X-Forwarded-Proto $scheme;
           proxy_set_header Accept-Encoding "";
           add_header Front-End-Https on;

           proxy_redirect off;
           proxy_buffering off;
           proxy_request_buffering off;

           # Long timeout for persistent Tor connections
           proxy_read_timeout 36000s;
           proxy_send_timeout 36000s;

           # Disable logging for the secret path (privacy)
           access_log off;
           error_log off;
       }
   }

   # HTTP to HTTPS redirect
   server {
       listen 80;
       listen [::]:80;
       server_name your-domain.com;
       return 301 https://$host$request_uri;
   }
   ```

4. **Enable the site and test**

   **Enable Nginx site and test configuration**

   ```bash
   sudo ln -sf /etc/nginx/sites-available/tor-bridge.conf \
       /etc/nginx/sites-enabled/
   sudo nginx -t && sudo systemctl reload nginx
   ```

**Comparison: What censors see vs What actually happens**

**What censors see**

**Normal HTTPS website**

A `GET /` request to your domain returns a plain HTML page. DPI sees standard TLS 1.3 traffic to a registered domain. Nothing suspicious.

```
HTTP/1.1 200 OK
Content-Type: text/html
<html>Welcome...</html>
```

**What actually happens**

**WebTunnel bridge connection**

A `GET /SECRET_PATH` with a `Connection: Upgrade` header reaches the WebTunnel server, which establishes a persistent tunnel for Tor traffic — all inside the same TLS session.

```
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
[Tor traffic flows through the tunnel]
```

---

## Step 5: Configure Port Forwarding

If your server is behind a router (common in home setups), you need to forward the following ports from your router's public IP to your server's local IP:

**Required Port Forwards**

| Port | Protocol | Purpose | Required? |
| --- | --- | --- | --- |
| **9001** | TCP | ORPort — relay-to-relay communication | Yes (always) |
| **4443** | TCP | obfs4 — obfuscated transport | Yes (if running obfs4) |
| **443** | TCP | HTTPS — WebTunnel via Nginx | Yes (if running WebTunnel) |

The exact steps depend on your router. Here's the general process:

**Generic Router Port Forwarding**

1. **Access your router's admin panel** (usually at `192.168.0.1` or `192.168.1.1`)

2. **Find the port forwarding section** (often under "NAT", "Firewall", or "Port Forwarding")

3. **Create rules for each port:**

   - **External port**: 9001 → **Internal IP**: your server's LAN IP → **Internal port**: 9001
   - **External port**: 4443 → **Internal IP**: your server's LAN IP → **Internal port**: 4443
   - **External port**: 443 → **Internal IP**: your server's LAN IP → **Internal port**: 443 (if not already forwarded)

4. **Save and apply** the changes

### Server Firewall (UFW)

If you're using UFW (Uncomplicated Firewall) on your server:

**Configure UFW for Tor bridge ports**

```bash
sudo ufw allow 9001/tcp comment "Tor ORPort"
sudo ufw allow 4443/tcp comment "Tor obfs4 Bridge"
# Port 443 is likely already open if you run Nginx
sudo ufw allow 443/tcp comment "HTTPS"
sudo ufw reload
```

If using `iptables` directly:

**Configure iptables for Tor bridge ports**

```bash
sudo iptables -A INPUT -p tcp --dport 9001 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 4443 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
```

### IPv6: No NAT Needed

Unlike IPv4, IPv6 addresses are **globally routable** — there is no NAT. This means your server's IPv6 address is directly reachable from the internet without port forwarding. However, you still need to:

1. **Allow the ports in your server's firewall** (UFW handles both IPv4 and IPv6 automatically)
2. **Allow the ports in your router's IPv6 firewall** — most routers have a separate IPv6 firewall that blocks all incoming traffic by default

**Warning — Router IPv6 Firewall**

Most routers **block all inbound IPv6** by default (unlike IPv4 where only NAT provides the "firewall"). You need to create explicit **allow rules** in your router's IPv6 firewall for TCP ports 9001 and 4443 to reach your server's IPv6 address. No destination NAT is needed — just firewall accept rules.

**Verify IPv6 firewall rules**

```bash
# Verify your server's IPv6 address
ip -6 addr show scope global | grep inet6

# Verify IPv6 firewall rules on the server
sudo ip6tables -L INPUT -n | grep -E "9001|4443"

# Test IPv6 reachability from another machine
nc -6zv YOUR_IPV6_ADDRESS 9001
```

---

## Step 6: AppArmor Configuration

On Debian/Ubuntu systems, AppArmor may restrict what Tor and obfs4proxy can do. You need to ensure the obfs4proxy profile allows execution:

**Configure AppArmor for obfs4proxy**

```bash
# Check if the profile exists
sudo aa-status 2>/dev/null | grep obfs4proxy

# If restricted, set to complain mode
sudo aa-complain /usr/bin/obfs4proxy 2>/dev/null

# Or create an explicit allow rule
echo "/usr/bin/obfs4proxy mr," | sudo tee -a /etc/apparmor.d/local/system_tor
sudo apparmor_parser -r /etc/apparmor.d/system_tor
```

---

## Step 7: Start and Verify

**Launch the Bridge**

1. **Start Tor**

   **Restart Tor service**

   ```bash
   sudo systemctl restart tor
   ```

2. **Check the logs for successful bootstrap**

   **Watch Tor bootstrap process**

   ```bash
   sudo journalctl -u tor -f --no-pager | head -30
   ```

   Look for these key messages:

   **Successful bootstrap output**

   ```text
   Bootstrapped 0% (starting): Starting
   Bootstrapped 5% (conn): Connecting to a relay
   Bootstrapped 10% (conn_done): Connected to a relay
   Bootstrapped 14% (handshake): Handshaking with a relay
   Bootstrapped 15% (handshake_done): Handshake with a relay done
   Bootstrapped 75% (enough_dirinfo): Loaded enough directory info to build circuits
   Bootstrapped 90% (ap_handshake_done): Handshake finished with a relay to build circuits
   Bootstrapped 95% (circuit_create): Establishing a Tor circuit
   Bootstrapped 100% (done): Done
   Self-testing indicates your ORPort 86.x.x.x:9001 is reachable from the outside. Excellent.
   Registered server transport 'obfs4' at '0.0.0.0:4443'
   Registered server transport 'webtunnel' at '127.0.0.1:15000'
   ```

3. **Verify all three processes are running**

   **Check that Tor, obfs4proxy, and webtunnel-server are running**

   ```bash
   ps aux | grep -E "(tor|obfs4|webtunnel)" | grep -v grep
   ```

   You should see three processes: `tor`, `obfs4proxy`, and `webtunnel-server`.

4. **Verify all ports are listening**

   **Check listening ports for Tor bridge**

   ```bash
   ss -tlnp | grep -E "(:443|9001|4443|15000|9051|9052)"
   ```

   **Output — Expected listening ports**

   ```text
   LISTEN  0  511    0.0.0.0:443    0.0.0.0:*  users:(("nginx",pid=...,fd=...))
   LISTEN  0  4096   0.0.0.0:9001   0.0.0.0:*  users:(("tor",pid=...,fd=...))
   LISTEN  0  4096   0.0.0.0:4443   0.0.0.0:*  users:(("obfs4proxy",pid=...,fd=...))
   LISTEN  0  4096 127.0.0.1:15000  0.0.0.0:*  users:(("webtunnel-ser",pid=...,fd=...))
   LISTEN  0  4096 127.0.0.1:9051   0.0.0.0:*  users:(("tor",pid=...,fd=...))
   LISTEN  0  4096   0.0.0.0:9052   0.0.0.0:*  users:(("tor",pid=...,fd=...))
   ```

5. **Retrieve your bridge fingerprint**

   **Get bridge fingerprint**

   ```bash
   cat /var/lib/tor/fingerprint
   ```

### Verify External Reachability

From a different machine (or using an online port checker), verify that your ports are accessible:

**Test ORPort reachability from another machine**

```bash
# From another machine:
nc -zv YOUR_PUBLIC_IP 9001
nc -zv YOUR_PUBLIC_IP 4443
curl -I https://your-domain.com/
```

**Tip — The ORPort Self-Test**

Tor performs its own reachability test during bootstrap. If you see `Self-testing indicates your ORPort is reachable from the outside`, you're good. If the self-test fails, double-check your port forwarding rules and firewall configuration.

---

## Step 8: Monitoring with Prometheus and Grafana

Tor exposes a rich set of metrics via its `MetricsPort` in Prometheus format. These metrics let you monitor bandwidth usage, connections, circuit building, and relay health in real time.

### Configure Prometheus Scraping

Add a scrape target for your Tor bridge in your Prometheus configuration:

**File: `prometheus.yml` (snippet)**

```yaml
scrape_configs:
  - job_name: 'tor-bridge'
    scrape_interval: 30s
    scrape_timeout: 10s
    metrics_path: '/metrics'
    static_configs:
      - targets: ['YOUR_SERVER_IP:9052']
        labels:
          instance: 'tor-bridge'
          nickname: 'YourBridgeName'
```

**Info — Firewall for Metrics Port**

The MetricsPort (9052) should **only** be accessible from your monitoring server, not the public internet. Use `MetricsPortPolicy accept YOUR_MONITORING_IP/32` in `torrc` and restrict access via firewall:

**Restrict MetricsPort to monitoring server**

```bash
sudo ufw allow from YOUR_MONITORING_IP to any port 9052 proto tcp \
  comment "Prometheus: Tor Bridge Metrics"
```

### Key Metrics to Monitor

Tor exposes dozens of Prometheus metrics. Here are the most important ones for a bridge operator:

**Essential Tor Prometheus Metrics**

| Metric | Type | What It Tells You |
| --- | --- | --- |
| `tor_relay_connections_total` | Counter | Total connections by type and direction (inbound/outbound, OR/directory) |
| `tor_relay_traffic_bytes` | Counter | Total bytes read/written — your bandwidth contribution to the network |
| `tor_relay_flag` | Gauge | Directory flags assigned to your relay (Stable, Running, Valid, etc.) |
| `tor_relay_circuits_total` | Gauge | Number of active circuits by state (opened, closed, etc.) |
| `tor_relay_streams_total` | Counter | Streams processed — a proxy for actual user connections |
| `tor_relay_load_oom_bytes_total` | Counter | Out-of-memory events — if this increases, add more RAM |
| `tor_relay_load_tcp_exhaustion_total` | Counter | TCP port exhaustion — if this increases, tune kernel network settings |
| `process_resident_memory_bytes` | Gauge | Current RAM usage of the Tor process |
| `process_cpu_seconds_total` | Counter | CPU time consumed by Tor |

### Grafana Dashboard

You can build a comprehensive Grafana dashboard with panels for:

1. **Bandwidth** — `rate(tor_relay_traffic_bytes[5m])` — real-time throughput in/out
2. **Connections** — `tor_relay_connections_total` by type — who's connecting
3. **Relay Flags** — `tor_relay_flag` — has the network recognized your bridge?
4. **Circuits** — `tor_relay_circuits_total` — how many circuits are active
5. **System Resources** — `process_resident_memory_bytes`, `process_cpu_seconds_total`
6. **Health** — `tor_relay_load_oom_bytes_total`, `tor_relay_load_tcp_exhaustion_total`

**Tip — Relay Flags Timeline**

After starting your bridge, relay flags will appear gradually:

- **Running**: ~1 hour after starting
- **Valid**: ~1 hour
- **Stable**: ~7 days of continuous uptime
- **Guard**: ~8 days (if applicable)

Be patient — the Tor directory authorities take time to trust new relays. Keep your bridge running consistently and the flags will come.

### Nyx: Terminal-Based Monitoring

[Nyx](https://nyx.torproject.org/) is a terminal UI for monitoring Tor in real time. It connects to the ControlPort and shows bandwidth graphs, connections, circuits, and configuration.

**Install and run Nyx**

```bash
sudo apt install -y nyx
sudo -u debian-tor nyx
```

---

## Step 9: CrowdSec Integration (If Applicable)

If you run [CrowdSec](https://www.crowdsec.net/) on the same server (common when running Nginx), you'll face a challenge: CrowdSec is designed to block malicious IPs, but **Tor bridge clients may trigger false positives**. Legitimate Tor users connecting to your bridge could get blocked by your own IDS.

**Warning — The Problem**

CrowdSec's firewall bouncer adds a `CROWDSEC_CHAIN` at the top of your iptables INPUT chain, and the Nginx bouncer checks every request against a blocklist. Both can inadvertently block legitimate Tor bridge clients.

The solution is a **three-layer bypass** that ensures Tor traffic is never blocked:

### Layer 1: iptables — Bypass the Firewall Bouncer

Insert ACCEPT rules for Tor-specific ports **before** the CROWDSEC_CHAIN:

**Add iptables bypass rules for Tor ports**

```bash
# IPv4
sudo iptables -I INPUT 1 -p tcp --dport 4443 -j ACCEPT  # obfs4
sudo iptables -I INPUT 1 -p tcp --dport 9001 -j ACCEPT  # ORPort

# IPv6
sudo ip6tables -I INPUT 1 -p tcp --dport 4443 -j ACCEPT
sudo ip6tables -I INPUT 1 -p tcp --dport 9001 -j ACCEPT
```

**Info — Why Not Port 443?**

Port 443 is shared with all your HTTPS services, not just Tor. Bypassing CrowdSec for **all** HTTPS traffic would create a security hole. Instead, the WebTunnel bypass for port 443 is handled at the Nginx level (Layer 2), where we can filter by domain name.

### Layer 2: Nginx — Bypass the Lua Bouncer

If your CrowdSec Nginx bouncer uses a `rewrite_by_lua_block` at the `http {}` level, add an exception for your Tor domain:

**File: `/etc/nginx/conf.d/crowdsec_nginx.conf` (modified section)**

```lua
rewrite_by_lua_block {
    local cs = require "plugins.crowdsec.lib.crowdsec"

    -- Bypass CrowdSec for Tor bridge domain
    if ngx.var.host == "your-tor-domain.com" then
        -- Skip CrowdSec check entirely for Tor bridge traffic
    else
        local ok, err = cs.Allow(ngx.var.remote_addr)
        if not ok then
            ngx.exit(ngx.HTTP_CLOSE)
        end
    end
}
```

### Layer 3: CrowdSec Parser Whitelist

Even with the above, CrowdSec will still **parse** your Tor logs and may create local decisions. Create a parser whitelist so it ignores all traffic to your Tor domain:

**File: `/etc/crowdsec/parsers/s02-enrich/tor-bridge-whitelist.yaml`**

```yaml
name: custom/tor-bridge-whitelist
description: "Ignore all traffic to the Tor bridge domain"
whitelist:
  reason: "Tor bridge traffic — legitimate by definition"
  expression:
    - evt.Parsed.target_fqdn == 'your-tor-domain.com'
```

For CrowdSec to detect the `target_fqdn`, your Nginx logs must include the `$host` variable. Create a custom log format:

**File: `/etc/nginx/conf.d/tor_log_format.conf`**

```nginx
# Log format with vhost prefix for CrowdSec target_fqdn detection
log_format combined_vhost
    '$host $remote_addr - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent"';
```

Then use it in your Tor server block:

```nginx
access_log /var/log/nginx/tor.access.log combined_vhost;
```

After making changes, reload both services:

**Reload CrowdSec and Nginx**

```bash
sudo systemctl reload crowdsec
sudo systemctl reload nginx
```

### Persist iptables Rules Across Reboots

The iptables bypass rules would be lost on reboot. Create a systemd service to re-apply them:

**File: `/etc/systemd/system/tor-bypass-crowdsec.service`**

```ini
[Unit]
Description=Tor Bridge — iptables bypass for CrowdSec
After=crowdsec-firewall-bouncer.service
Wants=crowdsec-firewall-bouncer.service

[Service]
Type=oneshot
RemainAfterExit=yes

# IPv4: Accept Tor traffic before CROWDSEC_CHAIN
ExecStart=/bin/sh -c '\
  iptables  -C INPUT -p tcp --dport 4443 -j ACCEPT 2>/dev/null || \
  iptables  -I INPUT 1 -p tcp --dport 4443 -j ACCEPT; \
  iptables  -C INPUT -p tcp --dport 9001 -j ACCEPT 2>/dev/null || \
  iptables  -I INPUT 1 -p tcp --dport 9001 -j ACCEPT'

# IPv6: Same rules
ExecStart=/bin/sh -c '\
  ip6tables -C INPUT -p tcp --dport 4443 -j ACCEPT 2>/dev/null || \
  ip6tables -I INPUT 1 -p tcp --dport 4443 -j ACCEPT; \
  ip6tables -C INPUT -p tcp --dport 9001 -j ACCEPT 2>/dev/null || \
  ip6tables -I INPUT 1 -p tcp --dport 9001 -j ACCEPT'

[Install]
WantedBy=multi-user.target
```

**Enable the iptables persistence service**

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now tor-bypass-crowdsec.service
```

---

## How do the bridge's components fit together?

The bridge is one Tor process reachable through two pluggable transports, obfs4 and WebTunnel, which the router exposes with IPv4 port forwards and IPv6 firewall rules while CrowdSec bypass rules keep those ports open; both transports hand their connections to Tor over ExtORPort, Tor talks outbound to the network, and Prometheus scrapes the MetricsPort that Grafana draws from.

Here's the complete architecture of what you've built:

**Complete Tor Bridge Architecture (Dual-Stack IPv4 + IPv6)**

```mermaid
flowchart TB
    subgraph Internet["🌍 Censored Users"]
        User1["Tor Client<br/>(obfs4)"]
        User2["Tor Client<br/>(WebTunnel)"]
    end

    subgraph Router["🔧 Router"]
        FW4["IPv4 Port Forward<br/>9001, 4443, 443"]
        FW6["IPv6 Firewall<br/>Accept 9001, 4443"]
    end

    subgraph Server["🖥️ Linux Server"]
        subgraph CrowdSec["🛡️ CrowdSec Bypass"]
            IPT["iptables / ip6tables"]
            LUA["Nginx Lua"]
        end

        OB["obfs4proxy<br/>:4443"]

        subgraph Nginx["📦 Nginx :443"]
            CAM["Camouflage Page<br/>(GET /)"]
            WTP["WebTunnel Proxy<br/>(GET /secret)"]
        end

        WT["webtunnel-server<br/>:15000"]
        TOR["🧅 Tor Process<br/>ORPort :9001 · IPv4 + IPv6"]
        PROM["MetricsPort :9052"]
    end

    TorNet["🧅 Tor Network"]

    subgraph Monitor["📊 Monitoring"]
        Prometheus
        Grafana
    end

    User1 -->|":4443 obfs4"| FW4
    User2 -->|":443 HTTPS"| FW4
    User1 -.->|":4443 IPv6"| FW6

    FW4 -->|":4443"| IPT
    FW4 -->|":443"| LUA
    FW6 -.->|"direct"| IPT

    IPT --> OB
    LUA --> CAM
    LUA --> WTP
    WTP --> WT

    OB -->|"ExtORPort"| TOR
    WT -->|"ExtORPort"| TOR

    TOR --> TorNet

    PROM --> Prometheus
    Prometheus --> Grafana
```

---

## How do you confirm the bridge is really working?

After everything is set up, run through this checklist to confirm your bridge is fully operational:

**Post-Installation Verification**

- Tor service is active: `systemctl is-active tor`
- Bootstrap at 100%: `journalctl -u tor | grep "Bootstrapped 100%"`
- IPv4 ORPort self-test passed: `grep "is reachable from the outside" /var/log/tor/notices.log`
- IPv6 ORPort self-test passed (if configured): look for your IPv6 address in the reachability message
- Three processes running: `tor`, `obfs4proxy`, `webtunnel-server`
- Five ports listening: 9001, 4443, 15000, 9051, 9052
- IPv6 port listening (if configured): `ss -tlnp6 | grep 9001`
- Nginx config valid: `nginx -t`
- Camouflage page returns HTTP 200: `curl -I https://your-domain.com/`
- WebTunnel path returns 502: `curl -I https://your-domain.com/SECRET` (expected — requires proper protocol)
- Prometheus scraping: `curl http://localhost:9052/metrics | head`
- Firewall rules allow Tor ports: `ufw status | grep -E "9001|4443"`
- Bridge fingerprint exists: `cat /var/lib/tor/fingerprint`
- **Warning** — Relay flags appear (patience!): Running and Valid in about an hour, Stable around 7 days and Guard around 8
- **Optional** — IPv6 reachability from external: `nc -6zv YOUR_IPV6 9001`
- **Optional** — CrowdSec bypass working (if applicable): `cscli explain` with a test log line

---

## What does a bridge need once it is running?

A running bridge needs three things from you: patience, because bootstrap, descriptor publication and BridgeDB distribution take one to two days and the Stable flag about a week; monitoring, because the metrics it exposes are the only way to know it is still helping anyone; and routine maintenance, meaning updates, uptime and an occasional check that its ports are still reachable from outside.

### What happens after you start the bridge?

- **0–1 hour — Bootstrap & Self-Test**: Tor connects to the network, downloads the consensus, and tests if your ORPort is reachable. If the self-test passes, your bridge is operational.
- **1–24 hours — Descriptor Publication**: Tor publishes your bridge descriptor to the Bridge Authority. Your bridge becomes known to BridgeDB but is not yet distributed to users.
- **24–48 hours — BridgeDB Distribution**: BridgeDB starts distributing your bridge line to users who request bridges. You may start seeing your first client connections.
- **1–7 days — Flag Accumulation**: Directory authorities assign flags based on your bridge's behavior. 'Running' and 'Valid' come first; 'Stable' requires ~7 days of continuous uptime.
- **Ongoing — Steady State**: Your bridge is fully integrated into the Tor network. Monitor metrics, keep software updated, and maintain uptime for maximum impact.

### Maintenance Best Practices

**Ongoing Maintenance**

- **Keep Tor updated**: Check for updates weekly — `sudo apt update && sudo apt upgrade tor`
- **Monitor logs**: Check `/var/log/tor/notices.log` for warnings and errors
- **Watch metrics**: Set up Grafana alerts for unusual bandwidth drops or connection failures
- **Maintain uptime**: The Tor network penalizes relays that go offline frequently
- **Warning** — **Never delete** `/var/lib/tor/pt_state/` — this contains your obfs4 keys. Losing them means getting a new bridge identity
- **Warning** — **Dynamic IP?** If your public IP changes, the obfs4 bridge line becomes invalid. WebTunnel is unaffected (uses domain name). Consider a DDNS service for obfs4.
- **Optional** — **Renew TLS certificates**: If using Let's Encrypt with auto-renewal, this is handled automatically

### Backup Critical Files

These files contain your bridge's identity. Losing them means starting over with a new fingerprint:

**Backup critical Tor bridge files**

```bash
sudo tar czf /root/tor-bridge-backup-$(date +%Y%m%d).tar.gz \
  /var/lib/tor/fingerprint \
  /var/lib/tor/keys/ \
  /var/lib/tor/pt_state/ \
  /etc/tor/torrc \
  /etc/nginx/sites-available/tor-bridge.conf
```

---

## Troubleshooting

**Something not working? Let's diagnose:**

**If: Tor fails to bootstrap (stuck below 100%)**

Check if your server can reach the Tor network:

**Test Tor network connectivity**

```bash
curl -s https://check.torproject.org/ | grep -i congratulations
```

If this fails, your ISP may be blocking Tor. Check DNS resolution and try using public DNS servers (8.8.8.8, 1.1.1.1).

**If: ORPort self-test fails**

This means Tor can't reach your server from the outside on port 9001. Verify:

1. Port forwarding is correctly configured on your router
2. `ORPort` is set to `0.0.0.0:9001` (not `127.0.0.1`)
3. Your server's firewall allows port 9001
4. Test from an external machine: `nc -zv YOUR_PUBLIC_IP 9001`

**If: obfs4proxy crashes or doesn't start**

Check AppArmor:

```bash
sudo aa-status | grep obfs4
```

If restricted, set to complain mode: `sudo aa-complain /usr/bin/obfs4proxy`

Also check permissions on the Tor data directory:

```bash
ls -la /var/lib/tor/pt_state/
```

**If: WebTunnel returns 502 on curl (this is actually normal!)**

A `502 Bad Gateway` when curling the secret path is **expected behavior**. WebTunnel requires a proper WebSocket/WebTunnel protocol handshake — a simple HTTP GET won't work. If Tor clients can connect, everything is fine.

**If: No relay flags appear after 24+ hours**

Check that:

1. Your bridge is publishing descriptors: `grep "publishing" /var/log/tor/notices.log`
2. The ORPort self-test passed
3. Your bridge has been running without interruption
4. Check [Tor Metrics Relay Search](https://metrics.torproject.org/rs.html) with your fingerprint (bridges may take 24–48h to appear)

**If: No client connections after several days**

This is normal for new bridges. BridgeDB distributes bridges gradually:

1. Confirm your bridge appears in Relay Search
2. Bridges are distributed based on demand — some transports get more users than others
3. WebTunnel bridges tend to get users faster in heavily censored regions
4. Give it up to a week before worrying

---

## How I run this

Four Tor nodes, which is the whole reason this guide exists rather than being a
summary of the manual.

Two bridges: one in Valencia, on the same server that serves this page, and one
in Alicante on a mini PC at home — that second one sits on a domestic fibre line
behind the MikroTik router, which is why the NAT and port-forwarding section
above is written the way it is. The two middle relays are IONOS VPS instances,
one in London and one in Madrid, because a relay wants stable bandwidth and a
residential line does not reliably provide it.

Honestly, there is no incident to report. Nothing dramatic has happened: no
takedown notice, no abuse complaint, no visit from anyone. The bridge and the
relays have simply run. That is worth stating plainly, because the question
people actually ask before running one is "what happens to me if I do this",
and the answer here, from a domestic line in Spain, has been: nothing.

Two things did surprise me, and both are in this guide because of it.

The first is how long the consensus flags take. I expected the bridge to be
"working" within the hour, and in the narrow sense it was — Running and Valid
arrive quickly. Stable took about a week here, and Guard a little longer. Those
are observations from this deployment, not a schedule: the directory authorities
assign Stable from a weighted mean time between failures rather than a plain
uptime counter, and Guard additionally wants Stable, Fast, familiarity and
enough bandwidth, so your dates will differ. The practical lesson holds either
way — a restart to change one config line sets the measurement back, so tinker
in the first hour and then leave it alone.

The second is that CrowdSec banned my own Tor clients before I thought to
exclude the bridge ports. From the IDS's point of view a bridge is exactly what
an attack looks like: many connections, many addresses, no pattern. It was
doing its job correctly and the result was still wrong, which is the failure
mode worth remembering — the rule that fires here is the same one described in
the [MikroTik honeypot](https://jmrp.io/blog/006-implementing-mikrotik-honeypot/) and
[nginx tarpit](https://jmrp.io/blog/005-implementing-tarpit-nginx/) write-ups, pointed at
traffic I actually wanted.

---

## The Network Needs You

Running a Tor bridge is one of the most impactful things you can do for internet freedom. Every bridge you operate is a **direct lifeline** for people under censorship — journalists in Iran, activists in China, students in Russia, citizens in Myanmar. The technical barrier is modest; the human impact is immeasurable.

**Security rating: A+ (Excellent) — Your Contribution to Internet Freedom**

By running an obfs4 + WebTunnel bridge with proper monitoring, firewall hardening, and camouflage, you're providing one of the most valuable resources in the Tor ecosystem. The network has ~10,000 relays but only ~2,600 bridges — your bridge directly serves the most vulnerable users.

If you found this guide helpful, consider sharing it with others who might want to contribute. The more bridges we have, the harder it is for censors to block access to the free internet.

And if you want to go further: set up a dedicated server for **exit relays** — they're the most scarce and impactful resource in the entire Tor network. That's a guide for another day.

---

