Back to Blog
José Manuel Requena Plens

Running a Tor Bridge on Linux: Complete obfs4 & WebTunnel Guide

Guide to running obfs4 and WebTunnel Tor bridges on Linux with Nginx camouflage, firewall hardening, Prometheus monitoring, and CrowdSec.

Cover image for Running a Tor Bridge on Linux: Complete obfs4 & 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 — What You'll Build
  • A Tor bridge running two pluggable transports simultaneously: obfs4 (randomized traffic) and WebTunnel (HTTPS camouflage)
  • An Nginx reverse proxy that makes WebTunnel traffic indistinguishable from normal HTTPS browsing
  • Port forwarding and firewall rules for NAT environments
  • Prometheus metrics exportable to Grafana for real-time monitoring
  • CrowdSec bypass rules so your IDS doesn’t block legitimate Tor clients
  • A systemd-managed service stack that survives reboots

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. Milestone Onion Routing Invented

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

  2. Standard 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.

  3. Standard 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.

  4. Milestone Tor Project, Inc. Founded

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

  5. Standard Bridges Introduced

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

  6. Standard 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.

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

  8. Deprecated Snowden Revelations

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

  9. Standard WebTunnel Released

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

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

  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. Standard BridgeDB Distribution

    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. Milestone 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
  • 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

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.