TorThe Tor network uses onion routing to obscure IP addresses and browsing paths by relaying traffic through multiple volunteer-run nodes.Glossary → .onion + ENS + IPFS: How to Host a Website That Cannot Be Taken Down
A site on one domain and one server has one failure point. Seize the server, suspend the domain, or cut the proxy and the site disappears.
The fix is three layers: a clearnet VPS, a Tor .onion fallback, and an ENS + IPFS mirror. This guide builds all three.
Layer 1 - Clearnet Server (Primary)
The foundation is a no-KYC VPS paid in Monero. See the private server deployment guide for the full PM2 + Caddy walkthrough. The short version:
- Provider: FlokiNET (Iceland), 1984 Hosting (Iceland), or Frantech (Canada)
- Payment: Monero - no bank, no KYC
- Domain: registered via Njalla - they put their name in WHOIS, not yours
- Stack: Node.js + PM2 process manager + Caddy reverse proxy (auto HTTPS)
This layer handles 99% of traffic. The other two layers exist for the 1% scenario where this layer is taken down.
Layer 2 - Tor Hidden Service
A Tor hidden service gives your site a .onion address. Visitors connect through Tor, there is no exit node, and the server IP stays hidden from them.
Install Tor on your VPS
- Add the official Tor repository and install:
apt install tor -y
- Edit
/etc/tor/torrc- add these two lines:HiddenServiceDir /var/lib/tor/hidden_service/ HiddenServicePort 80 127.0.0.1:3000
- Restart Tor:
systemctl restart tor
- Get your .onion address:
cat /var/lib/tor/hidden_service/hostname
You'll see something likeabc123xyz789...onion. That's your permanent .onion address - it doesn't change unless you delete the key files.
Your site is now live at http://[your-address].onion accessible via Tor Browser or any Tor-connected client. The port 80 → 3000 mapping means Tor handles incoming requests on port 80 and forwards them to your app on port 3000 - no Caddy needed for .onion traffic.
/var/lib/tor/hidden_service/ are your .onion identity. Back them up. If the server is destroyed and you want to restore the same address, you need private_key and hostname from that directory.Vanity .onion addresses (optional)
Standard .onion addresses are random 56-character strings. Tools like mkp224o can brute-force a prefix (e.g., cunicula...) given enough time and CPU. A 6-character prefix takes minutes; 8 characters takes hours to days on a modern machine. Configure it in torrc the same way - just point HiddenServiceDir to the directory containing your generated keys.
| Property | Clearnet | Tor .onion |
|---|---|---|
| Requires domain registrar | Yes | No |
| Visitor IP logged by server | Yes (unless VPNA virtual private network encrypts traffic between your device and a provider-run server, hiding activity from local networks while shifting trust to the VPN operator.Glossary →) | No |
| Server IP visible to visitor | Yes | No |
| Accessible without Tor Browser | Yes | No |
| Survives domain seizure | No | Yes |
| Setup time | ~1 hour | ~10 minutes |
Layer 3 - ENS Domain + IPFS
The third layer is a static export pinned to IPFS and linked to an ENS domain. No web server or DNS is required. If at least one IPFS node still hosts the files, the mirror stays reachable.
/api/*), server-side rendering, and dynamic data do not work. The ENS/IPFS layer is a read-only mirror of your last static build - a fallback, not a full replacement.Step 1 - Generate a static export
In next.config.ts, add output: 'export':
const nextConfig = {
output: 'export',
// ...rest of config
};Run npm run build. Next.js generates a static out/directory - pure HTML, CSS, and JS. No server required.
Note: remove output: 'export' for your live server build. Keep a separate next.config.ipfs.ts and build with:
NEXT_CONFIG=next.config.ipfs.ts npx next build
Step 2 - Pin to IPFS via Pinata
- Create a Pinata account at pinata.cloud (email required - use a privacy email like SimpleLogin alias). Free tier gives 1GB.
- Upload your
out/folder as a directory. - Pinata returns a CID like
QmXxx...orbafybeig.... That's the IPFS content hash of your site.
For better privacy, use web3.storage (accepts ETH wallet login, no email required) or run your own IPFS node: npm install -g ipfs then ipfs add -r out/.
Step 3 - Register an ENS domain
- Go to
app.ens.domainsand connect a wallet (MetaMask or any WalletConnect-compatible wallet). - Search for your name (e.g.,
cunicula.eth). Registration is ~$5/year in ETH, paid on-chain. - Complete registration - you'll need two transactions: a commit and a register. Total gas: ~$5–15 on mainnet depending on congestion.
Step 4 - Set Content Hash
- In ENS Manager, go to your name → Edit Records.
- Set the Content Hash field to your IPFS CID:
ipfs://bafybeig...your-cid-here
- Save - one more transaction.
Your site is now live at https://cunicula.eth.limo (clearnet gateway) and directly at cunicula.eth in Brave Browser or any ENS-compatible browser. No server. No DNS. No registrar that can be pressured.
| Property | Traditional | ENS + IPFS |
|---|---|---|
| Requires hosting provider | Yes | No (pinning service optional) |
| Domain can be seized | Yes (ICANN) | No (on-chain, self-sovereign) |
| Supports dynamic API routes | Yes | No (static only) |
| Browser support (native) | All | Brave, MetaMask Flask |
| Clearnet accessible | Yes | Via .eth.limo gateway |
| Annual cost | $5–50 hosting | ~$5 ENS renewal |
Wiring All Three Together
Add links to all three access methods in your site footer or header so visitors know the fallback options exist:
Hardening the .onion Address
To advertise your .onion address to Tor-aware browsers, add anOnion-Location HTTP header in your Caddy config:
yourdomain.com {
reverse_proxy localhost:3000
header Onion-Location http://youraddress.onion{uri}
}Tor Browser will show a purple "Onion Available" badge in the address bar and let users switch automatically.
Operational Security Notes
- Never run Tor Browser on the same machine as your server. Traffic correlation attacks become trivially easy if you do.
- Back up
/var/lib/tor/hidden_service/offline. These files are your .onion identity. Without them, your address is gone. - Keep your ENS registration paid. ENS domains expire. Set a calendar reminder - or pay for 5+ years upfront.
- IPFS pinning is not permanent by default. Pinata and web3.storage will unpin inactive content. Re-pin on each rebuild.
- The .eth.limo gateway is centralised. It's a convenience layer for non-ENS browsers. If it goes down, ENS still works natively in Brave and other ENS-aware clients.
Follow the Money
The Tor Project receives ~60% of its funding from US government sources. The IPFS/ENS stack runs through Filecoin and Ethereum networks. Funding matters when you assess who controls the infrastructure.