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.

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.
- Onion Routing Invented Milestone
Syverson, Goldschlag, and Reed at the U.S. Naval Research Laboratory develop the concept of layered encryption for anonymous communication.
- 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.
- 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.
- Tor Project, Inc. Founded Milestone
The Tor Project becomes a 501(c)(3) nonprofit organization based in Massachusetts, ensuring long-term development and governance.
- Bridges Introduced Standard
Secret relay addresses (bridges) are developed to help users bypass firewalls in censored countries where the Tor directory is blocked.
- 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.
- 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.
- Snowden Revelations Deprecated
Edward Snowden's disclosures about global mass surveillance programs drive a massive surge in Tor adoption worldwide.
- WebTunnel Released Standard
A new pluggable transport that disguises Tor traffic as ordinary HTTPS, making it nearly impossible to block without collateral damage.
- 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.
Here’s what makes this secure:
| 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 (see also the system overview in the Tor specification):
- Circuit path selection: The client randomly selects relays, avoiding two relays in the same
/16subnet 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:
| 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 |
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.
| Transport | Disguise Method | Censorship Resistance | Speed | Deployment |
|---|---|---|---|---|
| obfs4 | Makes traffic look like random bytes | High — resists DPI fingerprinting | Fast | Widely deployed, easy to set up |
| 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 |
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 prerequisites
sudo apt updatesudo apt install -y apt-transport-https gpg curlAdd 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.gpgAdd the Tor repository
Replace
CODENAMEwith 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.listInstall Tor and obfs4proxy
sudo apt updatesudo apt install -y tor deb.torproject.org-keyring obfs4proxyVerify the installation:
tor --versionobfs4proxy -versionExpected outputTor version 0.4.9.6. 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 and a guide for building from source.
Install Go (if not already installed)
sudo apt install -y golang-goVerify that Go 1.21+ is installed:
go versionClone and build WebTunnel
cd /tmpgit clone https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/webtunnel.gitcd webtunnel/main/servergo build -o webtunnel-server .sudo cp webtunnel-server /usr/local/bin/sudo chmod +x /usr/local/bin/webtunnel-serverVerify 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:
# =============================================================
# 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 1Let’s break down the critical sections:
Key Configuration Options Explained
- BridgeRelay 1
Bridge modeTells 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 PortInternal communication channel between Tor and pluggable transports. 'auto' lets Tor choose a random local port.- DisableDebuggerAttachment 1
Anti-ptrace protectionPrevents debuggers from attaching to the Tor process. A simple but effective defense against local privilege escalation.- MaxMemInQueues 4096 MB
Memory cap for queuesLimits 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 statsEnables collection of statistics about connection directionality, useful for monitoring and debugging via MetricsPort.- DoSRefuseSingleHopClientRendezvous 1
DoS protectionRefuses rendezvous circuits from single-hop clients. These are commonly used in abuse and denial-of-service attacks against hidden services.- ServerTransportListenAddr obfs4
obfs4 listening addressThe port where obfs4proxy listens for incoming obfuscated connections. Must be reachable from the internet.- ServerTransportListenAddr webtunnel
WebTunnel listening addressBinds to 127.0.0.1 only because Nginx handles the public-facing HTTPS and reverse-proxies to this port.- MetricsPort
Prometheus endpointExposes 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:
7Zkw18j2NWGK9X7PiiPUQRJBUse this value in two places:
- The
torrc:ServerTransportOptions webtunnel url=https://your-domain.com/YOUR_SECRET - 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.
Create a camouflage website
This is what visitors (and censors) see when they visit your domain:
sudo mkdir -p /var/www/tor-camouflagecat << '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>EOFObtain a TLS certificate (if you don’t already have one)
sudo apt install -y certbot python3-certbot-nginxsudo certbot --nginx -d your-domain.comCreate 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; }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
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>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:
| 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:
Access your router’s admin panel (usually at
192.168.0.1or192.168.1.1)Find the port forwarding section (often under “NAT”, “Firewall”, or “Port Forwarding”)
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)
Save and apply the changes
Server Firewall (UFW)
If you’re using UFW (Uncomplicated Firewall) on your server:
If using iptables directly:
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:
- Allow the ports in your server’s firewall (UFW handles both IPv4 and IPv6 automatically)
- Allow the ports in your router’s IPv6 firewall — most routers have a separate IPv6 firewall that blocks all incoming traffic by default
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:
Step 7: Start and Verify
Start Tor
sudo systemctl restart torCheck the logs for successful bootstrap
sudo journalctl -u tor -f --no-pager | head -30Look for these key messages:
Successful bootstrap outputBootstrapped 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'Verify all three processes are running
ps aux | grep -E "(tor|obfs4|webtunnel)" | grep -v grepYou should see three processes:
tor,obfs4proxy, andwebtunnel-server.Verify all ports are listening
ss -tlnp | grep -E "(9001|4443|15000|9051|9052)"Expected listening portsLISTEN 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=...))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:
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:
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:
| 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:
- Bandwidth —
rate(tor_relay_traffic_bytes[5m])— real-time throughput in/out - Connections —
tor_relay_connections_totalby type — who’s connecting - Relay Flags —
tor_relay_flag— has the network recognized your bridge? - Circuits —
tor_relay_circuits_total— how many circuits are active - System Resources —
process_resident_memory_bytes,process_cpu_seconds_total - Health —
tor_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.
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:
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:
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:
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:
# 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:
Persist iptables Rules Across Reboots
The iptables bypass rules would be lost on reboot. Create a systemd service to re-apply them:
[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.targetArchitecture Overview
Here’s the complete architecture of what you’ve built:
Verification Checklist
After everything is set up, run through this checklist to confirm your bridge is fully operational:
- 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 explainwith a test log line
Operational Notes
What Happens After You Start
- 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.
- Descriptor Publication
Tor publishes your bridge descriptor to the Bridge Authority. Your bridge becomes known to BridgeDB but is not yet distributed to users.
- BridgeDB Distribution Standard
BridgeDB starts distributing your bridge line to users who request bridges. You may start seeing your first client connections.
- Flag Accumulation
Directory authorities assign flags based on your bridge's behavior. 'Running' and 'Valid' come first; 'Stable' requires ~7 days of continuous uptime.
- 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
- Keep Tor updated: Check for updates weekly —
sudo apt update && sudo apt upgrade tor - Monitor logs: Check
/var/log/tor/notices.logfor 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:
Troubleshooting
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.
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
- Tor network Tor Project
- Tor Wikipedia
- origins of Tor onion-router.net
- Paul Syverson Wikipedia
- onion routing Wikipedia
- Tor's full history Tor Project
- circuit construction Tor Project
- system overview tpo.pages.torproject.net
- Tor network has roughly **8,000 relays** but only about **2,000 bridges** Tor Metrics
- Pluggable transports arxiv.org
- obfs4 Tor Project
- WebTunnel Tor Project
- Snowflake browser extension snowflake.torproject.org
- Debian/Ubuntu repository Tor Project
- official setup instructions Tor Project
- building from source Tor Project
- Nyx nyx.torproject.org
- CrowdSec CrowdSec
- Tor Metrics Relay Search Tor Metrics
- MikroTik honeypot jmrp.io
- nginx tarpit jmrp.io