Stop. Before you expose your home lab to the internet
You’ve got Nextcloud running. Maybe a Matrix server. Or you’re thinking about it. And your brain immediately goes: “I’ll just use Cloudflare Tunnels and call it a day.”
Tunnels are fine. They work. But you’re still giving Cloudflare a clear view of what’s inside. They see every request, every endpoint, every database query screaming across their infrastructure. They’re not malicious, but they’re not your grandmother either.
Tor hidden services (onion addresses) flip this completely. Instead of exposing your IP to the world, you hand out a .onion domain. Nobody, not your ISP, not Cloudflare, not a curious script kiddie scanning the internet, knows where your server lives. They can’t even see it without your permission.
That’s the real deal. And setting it up is way less painful than you’d think.
What is a Tor hidden service, actually?
Your regular server is like a storefront: you put your address on the sign, people find you. A Tor hidden service is more like a speakeasy. You exist, but you’re not on the map. Your users connect through a series of encrypted relays that scramble their origin and destination until neither party knows the other’s real location.
Here’s what happens when someone visits your .onion address:
- Their Tor client (usually the Tor Browser) connects to your advertised “introduction points”: a handful of relays you’ve chosen.
- Your server (also connected to Tor) maintains connections to the same introduction points.
- They meet in the middle, shake hands through multiple layers of encryption, and traffic flows both ways.
- Your server’s real IP never touches the conversation.
From the perspective of someone snooping on traffic, it looks like random encrypted noise bouncing between random Tor nodes. No pattern. No origin. No destination.
That’s powerful. That’s also why it matters.
The threat model: who are you actually protecting against?
Before you obsess over onion addresses, ask yourself: what are you protecting against?
ISP surveillance. Your ISP can see that you’re connecting somewhere, but Tor hides the destination. They see you’re using Tor; they don’t see what you’re doing inside it.
Mass port scanners. Shodan, Censys, anyone renting a botnet, they can’t find your .onion address unless you publish it. Your home IP stays dark.
Casual snooping. Someone on your home WiFi, or a malicious middle man on your network, can’t intercept traffic to a Tor hidden service because it’s encrypted from your app all the way to the Tor network entry point.
What Tor hidden services don’t protect against:
- Metadata. If you’re the only person accessing your own password manager, Tor can’t hide that you’re the user. Use it carefully, and don’t log in from your clearnet IP.
- Bad operational security. If your app leaks your real IP in an error message, or your login page says “Welcome, KingPin’s Home Lab,” you’re doxxed.
- Timing attacks. If your server’s response times are unusual, or you only come online at 3 AM, an attacker with access to Tor node logs can correlate behavior.
- Compromised clients. If your Tor Browser is malware, you’re already lost.
Know your adversary. If you’re protecting against your ISP learning that you run a homelab, Tor is overkill; Cloudflare Tunnels already do that. If you’re protecting against the entire internet discovering you run a homelab, Tor is the answer.
Setting up a Tor hidden service
You’re going to run Tor on your server, point it at a local port where your app lives, and Tor will advertise a .onion address. Simple diagram:
Internet (Tor Browser) ↓ Tor Network ↓ Your Tor Daemon (SOCKS localhost:9050) ↓ Reverse Proxy (localhost:8080) ↓ Your App (localhost:3000)Here’s the torrc config:
# /etc/tor/torrc (or /usr/local/etc/tor/torrc on macOS)
# Listen on localhost onlySocksPort 9050 IsolateDestAddrControlPort 9051
# Turn on hidden service supportHiddenServiceDir /var/lib/tor/hidden_service/HiddenServicePort 80 127.0.0.1:8080HiddenServicePort 443 127.0.0.1:8443HiddenServiceVersion 3
# Keep logs readableLog notice syslog
# Performance tuning for a small home labClientOnly 0NumCPUs 2MaxOnionsPending 100Key points:
- HiddenServiceDir: where Tor stores your onion keypair. Guard this like a password. If someone copies it, they can impersonate your service.
- HiddenServicePort: maps Tor’s virtual ports to your local app. Most hidden services use 80 and 443 (standard HTTP/HTTPS) even though there’s no “real” internet traffic happening.
- HiddenServiceVersion 3: v3 addresses are longer (56 characters vs the old 16), use stronger crypto, and are the only option that still works. v2 was fully removed from Tor back in 2021, so there’s nothing to “touch” anymore, v3 is the default and only game in town.
After you restart Tor:
sudo systemctl restart torcat /var/lib/tor/hidden_service/hostnameYou’ll see something like:
a7x5k9m2j3b8c6d4e5f7g9h2i4j6k8l9m0n1p3q5r7s9t2u4v6w8x.onionThat’s your address. Share it like you’d share a phone number, carefully.
Running Tor in a container (easier)
If you’re a Docker person, here’s a docker-compose.yml that runs Tor and a reverse proxy side-by-side:
services: tor: image: osminogin/tor-simple volumes: - tor_data:/var/lib/tor - ./torrc:/etc/tor/torrc:ro ports: - "9050:9050" # SOCKS proxy (optional, for debugging) - "9051:9051" # Control port (optional) networks: - internal restart: unless-stopped
reverse_proxy: image: caddy:2-alpine volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro ports: - "127.0.0.1:8080:8080" networks: - internal depends_on: - tor restart: unless-stopped
your_app: image: your-nextcloud-or-whatever expose: - "3000" networks: - internal environment: - TRUST_PROXY=127.0.0.1 restart: unless-stopped
volumes: tor_data:
networks: internal: driver: bridgeThe torrc config sits in your compose directory:
SocksPort 9050ControlPort 9051HiddenServiceDir /var/lib/tor/hidden_service/HiddenServicePort 80 reverse_proxy:8080HiddenServicePort 443 reverse_proxy:8443HiddenServiceVersion 3Log notice stdoutCaddy acts as your reverse proxy, handling TLS termination and routing to your app:
localhost:8080 { reverse_proxy your_app:3000 encode gzip}
localhost:8443 { reverse_proxy your_app:3000 tls internal encode gzip}After startup, grab your onion address:
docker exec <container_id> cat /var/lib/tor/hidden_service/hostnameNetwork isolation: the boring but critical part
Here’s where most people mess up.
Your .onion service is hidden, but your clearnet IP is not. If someone finds both, they can correlate them and figure out where you live. So: never access your own hidden service from the clearnet IP.
Break it down:
- Tor service listens on 127.0.0.1 only. Nobody can reach it from outside the container or machine.
- Reverse proxy listens on 127.0.0.1:8080. Again, only local traffic.
- App is isolated to a Docker network. No external ports exposed except through the reverse proxy.
- All traffic from the internet to the .onion address flows through Tor’s introduction points. Your ISP, your router, Cloudflare: none of them see it.
The mistake people make: they run a clearnet site on 0.0.0.0:8080 alongside the hidden service. That’s hiring a forklift to move a couch. One port scan and your onion service is linked to your IP.
Getting a vanity .onion address
Default onion addresses are random garbage. Some people want something memorable: thesumguy.onion or whatever.onion.
You can generate vanity addresses with mkp224o, but heads up: it’s a brute-force search. Finding a 6-letter prefix takes a few hours on a modern CPU. Finding 8 letters? Days. Months for 10+.
mkp224o -d ~/onion_search/ "thesumguy"This searches for any address starting with “thesumguy”. When it finds one, it saves the keypair to ~/onion_search/thesumguy...onion/. Copy that to your Tor HiddenServiceDir and restart.
Fair warning: vanity addresses are slower to find than they sound, and they’re not necessary. Your random 56-character address is just as secure and way faster to set up. Vanity is ego. Security is the point.
Operational security: the ways you’ll accidentally doxx yourself
You’ve got a hidden service. Great. Now don’t blow it up.
Mistake #1: Using the same username everywhere.
If your .onion service requires a login, and you use “KingPin” as the username, and you also use “KingPin” on GitHub or Twitter, someone can connect the dots. Use a different persona for sensitive services.
Mistake #2: Logging your real IP.
Your app logs every request. If you or someone accessing from the clearnet accidentally hits the .onion service, that request gets logged. Now your app knows about both the clearnet and hidden service traffic. If logs leak, you’re connected.
Keep clearnet and hidden service completely separate. Different apps, different databases if possible. At minimum, different auth tokens.
Mistake #3: Syncing data between clearnet and hidden.
If you run the same Nextcloud instance on both your clearnet IP and a hidden service, they’re obviously the same person. Pick one. If you need both, run separate instances with separate data.
Mistake #4: Checking your hidden service from the clearnet.
Your browser history, your DNS lookups, your connection patterns, all of it can leak if you’re not careful. Access your hidden service only through Tor Browser. Not Firefox with a SOCKS proxy. Not a VPN. The Tor Browser, period.
Mistake #5: Leaving the hidden service keypair world-readable.
The HiddenServiceDir and its contents are sensitive. Make sure permissions are tight:
ls -la /var/lib/tor/hidden_service/# Should show: drwx------ (700)If someone gets that keypair, they can impersonate your service and serve malware to your users. That’s worse than being doxxed.
When to use a hidden service (and when not to)
Use Tor hidden services if:
- You want to hide your home IP from the internet entirely
- You’re hosting sensitive apps (a Matrix server for journalists, an exit node, etc.)
- You want a service that’s only accessible to people you trust (no public marketing)
- You’re concerned about ISP-level surveillance
Don’t use Tor hidden services if:
- You want your blog to rank in Google. Tor traffic doesn’t help SEO.
- You need high performance. Tor adds latency (seconds, not milliseconds).
- You want a public service that normal people can find. .onion is a niche.
- You’re protecting against a technically sophisticated attacker with access to your machine. Tor won’t help.
Tor is a tool. It solves a specific problem: hiding location and identity on the network. It doesn’t solve everything. If your threat model is “I don’t want my ISP to know I run a homelab,” Tor works. If it’s “I don’t want the FBI to find me,” Tor is a starting point, not the whole answer.
In short
Tor hidden services are not magic. They’re not paranoid. They’re a straightforward technical solution to a real problem: keeping your home IP off the internet.
Set up a Tor daemon, point it at your app, grab the onion address, and share it with people you trust. Keep your clearnet and hidden service separate. Don’t be stupid about opsec.
Everything else is just friction.