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.

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.
- Milestone Onion Routing Invented
Syverson, Goldschlag, and Reed at the U.S. Naval Research Laboratory develop the concept of layered encryption for anonymous communication.
- 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.
- 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.
- Milestone Tor Project, Inc. Founded
The Tor Project becomes a 501(c)(3) nonprofit organization based in Massachusetts, ensuring long-term development and governance.
- Standard Bridges Introduced
Secret relay addresses (bridges) are developed to help users bypass firewalls in censored countries where the Tor directory is blocked.
- 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.
- 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.
- Deprecated Snowden Revelations
Edward Snowden's disclosures about global mass surveillance programs drive a massive surge in Tor adoption worldwide.
- Standard WebTunnel Released
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 update sudo 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 update sudo apt install -y tor deb.torproject.org-keyring obfs4proxyVerify the installation:
tor --version obfs4proxy -versionTor 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 /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-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-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> EOFObtain a TLS certificate (if you don’t already have one)
sudo apt install -y certbot python3-certbot-nginx sudo 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:
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'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)"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=...))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
- 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.
- Descriptor Publication
Tor publishes your bridge descriptor to the Bridge Authority. Your bridge becomes known to BridgeDB but is not yet distributed to users.
- Standard BridgeDB Distribution
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.
- 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
- 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
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.