SSD Nodes Learn 🎉 VPS from $4.99/mo
Guides Matt ConnorBy Matt Connor · Updated 2026-08-12

Run a Tor bridge with obfs4 on a VPS

Run an obfs4 Tor bridge on one cheap VPS: torrc directives, port choice, firewall, the log lines that prove it works, and how users get your bridge.

What a Tor bridge is, and why it exists

A Tor bridge is an entry point into the Tor network whose address is not published in the public relay list. That list, called the consensus, is a signed document anyone can download, and a censor downloads it too. Blocking Tor from it takes an afternoon: fetch the consensus, then drop every address in it at the border. Bridges exist because the published list is the weak point. Bridge addresses are given out a few at a time, so no single request hands over the whole set.

An unlisted address is only half the answer. Deep packet inspection (DPI), which classifies traffic by its content instead of its address, recognises a Tor connection from the shape of its TLS (transport layer security) handshake. A censor with no list can still see "this looks like Tor" and drop the connection. A pluggable transport removes that signal. It wraps the Tor stream in something else on the client side, and your bridge unwraps it.

obfs4 is the transport most bridges run. It turns the stream into bytes with no header and no fixed handshake, so DPI has no pattern to match. It also authenticates the client. The cert= value inside a bridge line is a key the client must prove it holds before the bridge answers at all, which defeats active probing: a censor who connects to your address to test whether it speaks Tor gets no reply and learns nothing.

Which pluggable transport should you run?

  • obfs4 needs one VPS, two TCP ports, and no domain name. It is the easiest useful thing you can run, and the subject of this guide.
  • WebTunnel hides the connection inside ordinary HTTPS traffic to a real website. The Tor Project lists its requirements as a static IPv4 address, a domain you control, a working web server such as NGINX or Apache, a valid TLS certificate, and at least 1 GB of RAM with 4 GB recommended. It suits networks where random-looking traffic is itself suspicious, because a country that permits little beyond web browsing still permits HTTPS.
  • Snowflake is a different contribution. Volunteers run short-lived WebRTC proxies, so the entry points change constantly and there is no stable address for a censor to block. You do not operate a bridge for it. You run a proxy, and it needs no fixed address.

Start with obfs4. You can add a WebTunnel bridge later, on a second address: running both on one IP means a single blocked address takes out both.

What does running a bridge cost you?

ChartTor Project published minimum bandwidth, August 2026
The data behind this chart
[
  {
    "label": "Bridge, minimum",
    "min_upstream_mbit": 1
  },
  {
    "label": "Guard or middle relay, minimum",
    "min_upstream_mbit": 10
  },
  {
    "label": "Guard or middle relay, recommended",
    "min_upstream_mbit": 16
  }
]

As of August 2026 the Tor Project asks a bridge for at least 1 Mbit/s of upstream and downstream bandwidth. A guard or middle relay is asked for 10 Mbit/s, with 16 Mbit/s recommended. These are published requirements, not measurements. A new bridge usually sits far below its own minimum for weeks. The same requirements page asks a relay for at least 100 GByte of outbound traffic per month, which the smallest plans already cover, so read what a small VPS actually costs per month before you size anything larger.

The abuse surface is small, and this is the part people get wrong. A bridge is the first hop. Traffic leaving your server goes to another Tor relay, never to a website a user picked. Your IP address never appears in a stranger's web log as the source of a request, so the complaint mail that exit relay operators handle does not arrive here. Check your provider's acceptable use policy anyway, because some hosts treat any Tor service as a special case.

One thing not to do: convert an existing public relay into a bridge at the same address. The Tor Project's advice for that case is to change "IP address, name and fingerprint", because the old address is already in the consensus that censors download. A bridge that was a public relay last week is a bridge that is already on a blocklist.

Uptime matters more than speed. The relay requirements say that "if your relay is not running for more than 2 hours a day its usefulness is limited", and a bridge is worse off than a relay here, because each client has one address and no fallback. A restart drops every user on it. Set up a TCP port check in Uptime Kuma against the obfs4 port so you learn the day it stops answering.

Install Tor from the Tor Project repository

Distribution packages lag behind, and a bridge is security software that should be current. Add the project's own repository first.

sudo apt update
sudo apt install -y apt-transport-https gnupg wget lsb-release
wget -qO- https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --dearmor | sudo tee /usr/share/keyrings/deb.torproject.org-keyring.gpg >/dev/null

Now write the source file. The Suites: line must hold your release codename, so read it from the system instead of typing it from memory.

sudo tee /etc/apt/sources.list.d/tor.sources >/dev/null <<EOF
Types: deb deb-src
URIs: https://deb.torproject.org/torproject.org/
Suites: $(lsb_release -cs)
Components: main
Signed-By: /usr/share/keyrings/deb.torproject.org-keyring.gpg
EOF
sudo apt update
sudo apt install -y tor deb.torproject.org-keyring obfs4proxy

If apt update reports that the repository has no Release file for your codename, the Tor Project does not carry that release. Delete /etc/apt/sources.list.d/tor.sources, run sudo apt update again, and install the tor package your distribution ships. Everything below is identical.

The obfs4proxy package comes from Debian and Ubuntu themselves (version 0.0.14 in Debian 13, as of August 2026). Confirm where the binary landed, because its path goes into the config:

command -v obfs4proxy || command -v lyrebird

Upstream renamed the project to lyrebird, so a newer package may install /usr/bin/lyrebird instead. Use whichever path that command prints.

Configure the bridge in /etc/tor/torrc

BridgeRelay 1
ORPort 8443
ServerTransportPlugin obfs4 exec /usr/bin/obfs4proxy
ServerTransportListenAddr obfs4 0.0.0.0:9443
ExtORPort auto
ContactInfo you@example.com
Nickname PickANickname
BridgeDistribution any

Every one of those lines has a failure attached to it, so take them one at a time.

BridgeRelay 1 tells tor to send its descriptor to the bridge authority instead of the public consensus. This single line is what makes the relay unlisted.

ORPort is the real Tor port. It has to be reachable from the internet, because tor tests it and refuses to publish a descriptor until that test passes.

ServerTransportPlugin gives tor the command to run. tor starts obfs4proxy as a child process and talks to it over a pipe, so obfs4proxy has no service unit of its own and never shows up in systemctl status.

ServerTransportListenAddr fixes the port obfs4proxy listens on. Leave this line out and obfs4proxy picks a free port at startup, a different one after most restarts, so every bridge line you already handed out points at a port where nothing is listening. Those clients get a refused connection and stop trying.

ExtORPort auto opens the extended ORPort, a loopback channel obfs4proxy uses to hand finished connections back to tor together with the client's address. The Tor Project's setup guide includes it on every bridge, because without it the transport cannot report that address to tor.

ContactInfo and Nickname are both public. Use an address you will read, since it is how the Tor Project reaches you about a broken bridge, and pick a nickname that does not identify you if you would rather stay quiet.

BridgeDistribution chooses which distributor gives your address to users. The accepted values are https, email, telegram, settings, none and any. Use any for a first bridge and let the system decide. Use none for a private bridge you hand out yourself, which keeps the address out of public distribution entirely.

Why the port choice matters

Avoid 9001 for both ports. The Tor Project says so directly, because 9001 is the traditional ORPort and censors scan the internet for it. The two ports must also differ from each other, since tor and obfs4proxy each bind their own listener.

The strongest obfs4 port is 443. Outbound 443 is open on nearly every restricted network, and a long-lived connection to it looks like an ordinary web session. Binding below 1024 needs one extra step, because obfs4proxy does not run as root:

sudo setcap cap_net_bind_service=+ep /usr/bin/obfs4proxy
sudo systemctl edit tor@.service tor@default.service

Add these two lines in each editor that opens:

[Service]
NoNewPrivileges=no

The capability on its own is not enough. systemd's NoNewPrivileges stops a process from gaining any privilege its parent did not have, and a file capability is exactly that, so obfs4proxy fails to bind 443 while the setting stays on.

If you would rather skip that step, pick an unremarkable high port and write it down. Whatever you choose, do not change the obfs4 port later. A bridge line pins address, port, fingerprint and certificate together, so every copy already sitting in a user's browser breaks the moment the port moves.

Open the ports on both firewalls

sudo ufw allow 8443/tcp
sudo ufw allow 9443/tcp
sudo ufw status

Both ports need to be open, and most providers run a second firewall in their control panel that ufw knows nothing about. A rule that exists on the box but not in the panel produces a bridge that is never reachable and never publishes a descriptor. If either half of this is new, the ufw rules a fresh VPS needs and what a listening port actually is on Linux cover it. While you are there, lock down SSH with keys and a hardened sshd config. An unlisted bridge on a box with password SSH is still a box with password SSH.

Start it, then read the log

sudo systemctl enable --now tor.service
sudo systemctl restart tor.service
sudo journalctl -e -u tor@default

Debian and Ubuntu ship two units. tor.service is a small wrapper and tor@default.service is the process doing the work, which is why journalctl -u tor looks almost empty while the log you want sits under tor@default.

Two lines say it worked:

Self-testing indicates your ORPort is reachable from the outside. Excellent. Publishing server descriptor.
Registered server transport 'obfs4' at '0.0.0.0:9443'

The first means the reachability test passed and the descriptor went to the bridge authority. If it never appears, something between the internet and your server is dropping traffic to the ORPort. The second line must show the port you configured. A different port there means tor never applied ServerTransportListenAddr, and the usual cause is a mismatched transport name: it has to read obfs4 on both directives.

Confirm the two listeners exist:

sudo ss -lntp | grep -E 'tor|obfs4|lyrebird'

Where is my bridge line?

obfs4proxy writes a template into tor's data directory:

sudo cat /var/lib/tor/pt_state/obfs4_bridgeline.txt

That directory belongs to the tor user and is mode 700, so without sudo you get Permission denied. The file holds a line in this shape:

Bridge obfs4 <IP ADDRESS>:<PORT> <FINGERPRINT> cert=<CERTIFICATE> iat-mode=0

Replace <IP ADDRESS> with your server's public address, <PORT> with the obfs4 port and not the ORPort, and <FINGERPRINT> with the identity fingerprint tor wrote into its data directory:

sudo cat /var/lib/tor/fingerprint
sudo cat /var/lib/tor/hashed-fingerprint

The first file holds your nickname and the identity fingerprint that belongs in a bridge line. The second holds the hashed fingerprint, which is what you paste into Relay Search to see whether your bridge is running and roughly how many clients reach it. The two are not interchangeable. A bridge line carrying the hashed value does not match the identity key your bridge presents, so the client rejects the connection it just opened.

How does a bridge actually reach users?

You do not hand your bridge line to anyone. Once the descriptor reaches the bridge authority, the distribution system (rdsys, the successor to BridgeDB) assigns your bridge to one distributor, and users ask that distributor for bridges. As of August 2026 the routes are these:

  • The web form at bridges.torproject.org/options, which serves bridge lines after a captcha.
  • Email to bridges@torproject.org from a Gmail or Riseup address, which replies with bridge lines. The provider restriction exists because unlimited free accounts would let one censor enumerate every bridge.
  • The Telegram bot @GetBridgesBot. Send /start, then /obfs4 or /webtunnel.
  • Tor Browser itself, under Settings then Connection, where "Request bridges" fetches them over the moat channel.

A new bridge shows up in Relay Search about three hours after setup. Users take much longer: the Tor Project's own wording is that "It can take several days or weeks until you see a consistent set of users." A quiet first fortnight is normal, not a fault.

Setting BridgeDistribution none opts out of all of it. The bridge line is then yours to send to the people who need it, over a channel the censor is not reading.

When something does not work

No self-testing line in the log. The ORPort is not reachable. Test it from another machine with nc -vz your.ip 8443. A hang means packets are being dropped, so check ufw and the provider's panel. A refusal means tor is not listening, so check ss -lntp and read the log for a config error.

The registered transport shows a port you did not pick. tor ignored ServerTransportListenAddr. The transport name has to match the one in ServerTransportPlugin exactly, and both have to be obfs4.

obfs4proxy will not bind port 443. Confirm the capability with getcap /usr/bin/obfs4proxy, then confirm the override reached the unit with systemctl show tor@default -p NoNewPrivileges. If that prints NoNewPrivileges=yes, your drop-in went to a unit that is not running.

Nothing inside /var/lib/tor/pt_state/. tor never started the transport, which means the path in ServerTransportPlugin is wrong. Compare it against the output of command -v obfs4proxy.

Clients stopped connecting after a change. Any change to the address or the obfs4 port invalidates every bridge line already distributed. Check whether the server's public IP moved as well, which happens on a rebuild with some providers.

tor will not start at all. Run sudo -u debian-tor tor --verify-config -f /etc/tor/torrc. It parses the file, prints the line it objects to, and leaves the running service alone.

FAQ

Will my VPS provider complain about a Tor bridge?

A bridge is an entry point, so traffic leaving your server goes to other Tor relays and never to a site a user chose. Your IP address does not appear in anyone's web log as the source of a request, and that is what generates the complaints exit relay operators deal with. Hosting rules still vary, and some providers treat any Tor service as a special case, so read the acceptable use policy before you start and put an address you read into ContactInfo.

How much bandwidth does a Tor bridge use?

The published minimum is 1 Mbit/s up and down, against 10 Mbit/s for a guard or middle relay. Real use starts near zero, because your bridge only carries traffic for the users a distributor sends to it. If you want a hard ceiling, set RelayBandwidthRate and RelayBandwidthBurst in torrc.

Why has nobody connected to my new bridge?

A bridge takes about three hours to appear in Relay Search, and the Tor Project's guidance is that a consistent set of users takes several days or weeks. Check that the descriptor published, which is the self-testing line in journalctl -u tor@default, look up your hashed fingerprint in Relay Search, and confirm BridgeDistribution is not set to none.

Should I run obfs4 or WebTunnel?

Run obfs4 if this is your first bridge: one VPS, two ports, no domain, no certificate. Run WebTunnel where random-looking traffic is itself blocked, since it needs a domain you control, a real web server, a valid TLS certificate, and at least 1 GB of RAM. Put them on separate addresses if you run both, because one blocked IP would otherwise remove two bridges at once.

What happens if I change the obfs4 port later?

Every bridge line already distributed stops working. A bridge line pins the address, the port, the fingerprint and the certificate together, so a client holding the old line opens a connection to a port where nothing listens and gives up. The same applies when the server's public IP changes. Choose the port during setup and leave it alone.