Back to Blog
37 min

Running a Tor Bridge on Linux: obfs4 and WebTunnel Guide

One bridge running obfs4 and WebTunnel at once, with Nginx making the WebTunnel half indistinguishable from ordinary HTTPS. Running and Valid arrive within the hour; Stable and Guard took about a week here, because they are scored on sustained reliability rather than a plain uptime counter.

Cover image for Running a Tor Bridge on Linux: obfs4 and WebTunnel Guide

In 2026, over 2.5 million people use the Tor network every single day to access the free and open internet. 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,000 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

6 key points
  • 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 (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.

A Brief History

The origins of Tor trace back to the 1990s at the U.S. Naval Research Laboratory (NRL), where researchers Paul Syverson, David Goldschlag, and Michael Reed developed the concept of onion routing — a technique for anonymous communication over a computer network. You can read more about Tor’s full history on the official site.

  1. Onion Routing Invented Milestone

    Syverson, Goldschlag, and Reed at the U.S. Naval Research Laboratory develop the concept of layered encryption for anonymous communication.

  2. Tor Project Launched Standard

    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.

  3. EFF Begins Funding Standard

    The Electronic Frontier Foundation recognizes Tor's importance for digital rights and provides critical early funding. NRL releases the code to the public domain.

  4. Tor Project, Inc. Founded Milestone

    The Tor Project becomes a 501(c)(3) nonprofit organization based in Massachusetts, ensuring long-term development and governance.

  5. Bridges Introduced Standard

    Secret relay addresses (bridges) are developed to help users bypass firewalls in censored countries where the Tor directory is blocked.

  6. Tor Browser Development Begins Standard

    A dedicated browser that packages Tor into an easy-to-use application, dramatically lowering the barrier to entry for non-technical users.

  7. Arab Spring Milestone

    Tor plays a critical role as activists in Tunisia, Egypt, Libya, and Syria use it to organize protests and communicate securely despite government censorship.

  8. Snowden Revelations Deprecated

    Edward Snowden's disclosures about global mass surveillance programs drive a massive surge in Tor adoption worldwide.

  9. WebTunnel Released Standard

    A new pluggable transport that disguises Tor traffic as ordinary HTTPS, making it nearly impossible to block without collateral damage.

  10. Today

    ~8,000 relays, ~2,000 bridges, and 2.5+ million daily users across 200+ countries. The network is entirely volunteer-operated.

How Onion Routing Works

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.

DestinationExit RelayMiddle RelayGuard RelayUserDestinationExit RelayMiddle RelayGuard RelayUserEncrypts with 3 layers:Layer 3 (Guard key)Layer 2 (Middle key)Layer 1 (Exit key)Peels Layer 3Knows: User IPDoesn't know: DestinationPeels Layer 2Knows: Nothing useful(only prev & next hop)Peels Layer 1Knows: DestinationDoesn't know: User IP🧅🧅🧅 [3 layers encrypted]🧅🧅 [2 layers encrypted]🧅 [1 layer encrypted]📄 [Plaintext or HTTPS]
Onion routing: three layers of encryption through three relays

Here’s what makes this secure:

What Each Relay Knows
RelayKnowsDoesn’t Know
Guard (Entry)Your real IP addressWhat websites you’re visiting
MiddlePrevious and next relay onlyNeither your IP nor your destination
ExitThe destination websiteYour real IP address

Key technical details of Tor’s circuit construction (see also the system overview 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.

The Tor Ecosystem: Types of Volunteers

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

Tor Volunteer Roles
RoleWhat It DoesRisk LevelImpact
Guard RelayFirst hop in the circuit. Sees user IP but not destination.LowHigh — provides bandwidth
Middle RelayIntermediate hop. Sees nothing useful.Very LowMedium — adds bandwidth and path diversity
Exit RelayFinal hop. Connects to the destination on behalf of the user.HighVery High — the most needed and scarce resource
BridgeHidden entry point for users in censored countries. Not listed in the public Tor directory.LowCritical — directly enables censorship circumvention
Snowflake ProxyEphemeral WebRTC proxy that helps censored users reach the Tor network.Very LowMedium — easy to run, even in a browser tab
Directory AuthorityTrusted server that maintains network consensus.N/ACritical — only 9 exist, run by the Tor Project

Understanding 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 solve this by disguising Tor traffic as something else entirely.

Pluggable Transport Comparison
TransportDisguise MethodCensorship ResistanceSpeedDeployment
obfs4Makes traffic look like random bytesHigh — resists DPI fingerprintingFastWidely deployed, easy to set up
WebTunnelMimics HTTPS/WebSocket trafficVery High — looks like normal web browsingFastNewer, requires web server + domain
SnowflakeUses WebRTC through ephemeral proxiesVery High — proxies rotate constantlyVariableClient-side only (volunteers run browser proxies)
meekDisguises traffic as requests to cloud services (Azure, CDN)Extreme — blocking it means blocking cloud servicesSlowExpensive to maintain, last resort
Which should you run? Here's how to decide:
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.

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.

I don’t have a server, but I want to help right now

Install the Snowflake browser extension — it takes 30 seconds and turns your browser into a temporary proxy for censored users. Zero maintenance required.

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


Step 1: Install Tor from the Official Repository

Install Tor and Dependencies
  1. Install prerequisites

    sudo apt update
    sudo apt install -y apt-transport-https gpg curl
  2. Add the Tor Project GPG key

    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):

    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

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

    Verify the installation:

    tor --version
    obfs4proxy -version
    Expected output
    Tor version 0.4.9.6.
    obfs4proxy-0.0.14

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 and a guide for building from source.

Compile WebTunnel Server
  1. Install Go (if not already installed)

    sudo apt install -y golang-go

    Verify that Go 1.21+ is installed:

    go version
  2. Clone and build WebTunnel

    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

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

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

/​etc/​tor/​torrc
# =============================================================
# 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

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.

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:

echo $(cat /dev/urandom | tr -cd 'a-zA-Z0-9' | head -c 24)
Example output
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 called camouflage or domain fronting.

Set Up Nginx Camouflage
  1. Create a camouflage website

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

    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)

    sudo apt install -y certbot python3-certbot-nginx
    sudo certbot --nginx -d your-domain.com
  3. Create the Nginx server block

    /​etc/​nginx/​sites-available/​tor-bridge.conf
    # 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

    sudo ln -sf /etc/nginx/sites-available/tor-bridge.conf \
    /etc/nginx/sites-enabled/
    sudo nginx -t && sudo systemctl reload nginx
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
PortProtocolPurposeRequired?
9001TCPORPort — relay-to-relay communicationYes (always)
4443TCPobfs4 — obfuscated transportYes (if running obfs4)
443TCPHTTPS — WebTunnel via NginxYes (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:

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:

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
# 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:

# 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

    sudo systemctl restart tor
  2. Check the logs for successful bootstrap

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

    Look for these key messages:

    Successful bootstrap output
    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

    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

    ss -tlnp | grep -E "(9001|4443|15000|9051|9052)"
    Expected listening ports
    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

    cat /var/lib/tor/fingerprint

Verify External Reachability

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

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

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:

prometheus.yml (snippet)
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'

Key Metrics to Monitor

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

Essential Tor Prometheus Metrics
MetricTypeWhat It Tells You
tor_relay_connections_totalCounterTotal connections by type and direction (inbound/outbound, OR/directory)
tor_relay_traffic_bytesCounterTotal bytes read/written — your bandwidth contribution to the network
tor_relay_flagGaugeDirectory flags assigned to your relay (Stable, Running, Valid, etc.)
tor_relay_circuits_totalGaugeNumber of active circuits by state (opened, closed, etc.)
tor_relay_streams_totalCounterStreams processed — a proxy for actual user connections
tor_relay_load_oom_bytes_totalCounterOut-of-memory events — if this increases, add more RAM
tor_relay_load_tcp_exhaustion_totalCounterTCP port exhaustion — if this increases, tune kernel network settings
process_resident_memory_bytesGaugeCurrent RAM usage of the Tor process
process_cpu_seconds_totalCounterCPU time consumed by Tor

Grafana Dashboard

You can build a comprehensive Grafana dashboard with panels for:

  1. Bandwidthrate(tor_relay_traffic_bytes[5m]) — real-time throughput in/out
  2. Connectionstor_relay_connections_total by type — who’s connecting
  3. Relay Flagstor_relay_flag — has the network recognized your bridge?
  4. Circuitstor_relay_circuits_total — how many circuits are active
  5. System Resourcesprocess_resident_memory_bytes, process_cpu_seconds_total
  6. Healthtor_relay_load_oom_bytes_total, tor_relay_load_tcp_exhaustion_total

Nyx: Terminal-Based Monitoring

Nyx is a terminal UI for monitoring Tor in real time. It connects to the ControlPort and shows bandwidth graphs, connections, circuits, and configuration.

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

Step 9: CrowdSec Integration (If Applicable)

If you run CrowdSec 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.

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:

# 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

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:

/​etc/​nginx/​conf.d/​crowdsec_nginx.conf (modified section)
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:

/​etc/​crowdsec/​parsers/​s02-enrich/​tor-bridge-whitelist.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:

/​etc/​nginx/​conf.d/​tor_log_format.conf
# 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:

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

After making changes, reload both services:

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:

/​etc/​systemd/​system/​tor-bypass-crowdsec.service
[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
sudo systemctl daemon-reload
sudo systemctl enable --now tor-bypass-crowdsec.service

Architecture Overview

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

📊 Monitoring

🖥️ Linux Server

🔧 Router

🌍 Censored Users

📦 Nginx :443

🛡️ CrowdSec Bypass

:4443 obfs4

:443 HTTPS

:4443 IPv6

:4443

:443

direct

ExtORPort

ExtORPort

Tor Client

(obfs4)

Tor Client

(WebTunnel)

IPv4 Port Forward

9001, 4443, 443

IPv6 Firewall

Accept 9001, 4443

iptables / ip6tables

Nginx Lua

obfs4proxy

:4443

Camouflage Page

(GET /)

WebTunnel Proxy

(GET /secret)

webtunnel-server

:15000

🧅 Tor Process

ORPort :9001 · IPv4 + IPv6

MetricsPort :9052

🧅 Tor Network

Prometheus

Grafana

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

Verification Checklist

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
  • Relay flags appear (patience!): takes 1–72 hours depending on the flag
  • IPv6 reachability from external: nc -6zv YOUR_IPV6 9001
  • CrowdSec bypass working (if applicable): cscli explain with a test log line

Operational Notes

What Happens After You Start

  1. Bootstrap & Self-Test Standard

    Tor connects to the network, downloads the consensus, and tests if your ORPort is reachable. If the self-test passes, your bridge is operational.

  2. Descriptor Publication

    Tor publishes your bridge descriptor to the Bridge Authority. Your bridge becomes known to BridgeDB but is not yet distributed to users.

  3. BridgeDB Distribution Standard

    BridgeDB starts distributing your bridge line to users who request bridges. You may start seeing your first client connections.

  4. Flag Accumulation

    Directory authorities assign flags based on your bridge's behavior. 'Running' and 'Valid' come first; 'Stable' requires ~7 days of continuous uptime.

  5. Steady State Milestone

    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
  • Never delete /var/lib/tor/pt_state/ — this contains your obfs4 keys. Losing them means getting a new bridge identity
  • 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.
  • 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:

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:
Tor fails to bootstrap (stuck below 100%)

Check if your server can reach the Tor network:

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

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
obfs4proxy crashes or doesn’t start

Check AppArmor:

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:

ls -la /var/lib/tor/pt_state/
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.

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 with your fingerprint (bridges may take 24–48h to appear)
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 and nginx tarpit 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.

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 ~8,000 relays but only ~2,000 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.


Frequently asked questions

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 8,000 relays but only about 2,000 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.

Further Reading & Resources

  1. Tor network Tor Project
  2. Tor Wikipedia
  3. origins of Tor onion-router.net
  4. Paul Syverson Wikipedia
  5. onion routing Wikipedia
  6. Tor's full history Tor Project
  7. circuit construction Tor Project
  8. system overview tpo.pages.torproject.net
  9. Tor network has roughly **8,000 relays** but only about **2,000 bridges** Tor Metrics
  10. Pluggable transports arxiv.org
  11. obfs4 Tor Project
  12. WebTunnel Tor Project
  13. Snowflake browser extension snowflake.torproject.org
  14. Debian/Ubuntu repository Tor Project
  15. official setup instructions Tor Project
  16. building from source Tor Project
  17. Nyx nyx.torproject.org
  18. CrowdSec CrowdSec
  19. Tor Metrics Relay Search Tor Metrics
  20. MikroTik honeypot jmrp.io
  21. nginx tarpit jmrp.io