Blockscout Tor Hidden Service Bridge
A lightweight, production-ready setup to expose any HTTP service as a Tor hidden service (.onion address) using Docker Compose. This repository demonstrates exposing a remote Blockscout instance, but can be adapted for RPC providers, static dApp websites, indexer servers, or any HTTP-based service.
flowchart TD
A["👤 Tor Browser User"] -->|".onion request"| B["🌐 Tor Network"]
B -->|"Routes traffic via<br/>Tor hidden service protocol"| C["🐳 Tor Container<br/>(Port 80)<br/>━━━━━━━━━━━━━━<br/>Runs tor daemon<br/>Manages .onion address"]
C -->|"Forwards to internal network"| D["🐳 Nginx Container<br/>(Port 8080)<br/>━━━━━━━━━━━━━━<br/>Reverse proxy<br/>HTTP traffic handler"]
D -->|"HTTP proxy request"| E["🌍 Remote Service<br/>━━━━━━━━━━━━━━<br/>Blockscout: 5.9.87.214:80<br/>or RPC Provider: :8545<br/>or any HTTP service"]
E -->|"Response"| D
D -->|"Response"| C
C -->|"Via Tor network"| B
B -->|"Response"| A
- Simple Architecture: Separate Tor and Nginx containers for better isolation
- HTTP-Only Design: Lightweight setup without HTTPS certificate management overhead
- Single-Hop Mode: Optimized for speed when service anonymity is not required
- Universal: Works with any HTTP service (RPC providers, dApps, indexers, APIs)
- Persistent Keys: Supports using existing .onion addresses
- Production-Ready: Automatic restarts, health checks, and proper logging
- Security-Focused: Explicitly disables exit relay and control port to prevent abuse
- Onion-Location Ready: Built-in support for advertising .onion on clearnet sites
- CI/CD Ready: GitHub Actions workflow for automated testing (see CI.md)
This setup uses HTTP between containers and to the upstream service because:
- No certificate management complexity
- Lower overhead and resource usage
- The Tor network already provides end-to-end encryption
- Perfect for development and services where the backend is trusted
This setup is configured with:
HiddenServiceSingleHopMode 1
HiddenServiceNonAnonymousMode 1
Why use single-hop mode?
- Speed: Reduces circuit from 6 hops (3+3) to 3+1 hops, significantly lowering latency
- On-par with exit nodes: Similar speed to using a Tor exit node, but...
- No exit node censorship: Your service can't be blocked by exit node operators
- No eavesdropping: Unlike exit nodes, no third party can intercept traffic
- Use case: Ideal when you want Tor's censorship resistance without needing location anonymity
Trade-off: The server's IP address may be exposed to determined adversaries. Only use this mode if you don't need to hide the server's physical location.
ORPort 0 # No relay functionality
ExitRelay 0 # Not an exit node
ExitPolicy reject *:*
SocksPort 0 # No SOCKS proxy
This prevents:
- Your server from being used as a Tor relay (bandwidth abuse)
- Exit node liability (illegal content routing through your IP)
- Unauthorized access via SOCKS proxy
- Control port exploitation
blockscout-onion/
├── Dockerfile.tor # Tor container image
├── Dockerfile.nginx # Nginx container image
├── docker-compose.yml # Docker Compose configuration
├── torrc # Tor configuration
├── nginx.conf # Nginx proxy configuration
├── entrypoint-tor.sh # Tor startup script
├── public/ # Screenshots for documentation
│ ├── blockscout.png
│ └── status.png
├── tor_data/ # Persistent Tor data (auto-created)
│ └── hidden_service/ # Hidden service keys
│ ├── hostname # Your .onion address
│ ├── hs_ed25519_public_key
│ └── hs_ed25519_secret_key
└── README.md
git clone <your-repo-url>
cd blockscout-onionTo proxy a different service, edit nginx.conf:
upstream blockscout {
server your-service.com:8545; # Change to your service
}If you have existing hidden service keys:
mkdir -p tor_data/hidden_service
cp /path/to/your/hs_ed25519_public_key tor_data/hidden_service/
cp /path/to/your/hs_ed25519_secret_key tor_data/hidden_service/
cp /path/to/your/hostname tor_data/hidden_service/
chmod 700 tor_data/hidden_service
chmod 600 tor_data/hidden_service/*If you don't have keys, Tor will generate new ones automatically.
docker-compose up -d --build# Wait a moment for Tor to bootstrap, then:
cat tor_data/hidden_service/hostnameExample output:
7lxb5y5sikmt4nmsm37yq567q3zazgsxmyapjs7azssoaojxna6kftid.onion
Open Tor Browser and visit:
- Main service:
http://your-address.onion - Status page:
http://your-address.onion/status
To make Tor Browser users aware of your .onion service when they visit your clearnet site, add the Onion-Location header to your clearnet nginx server:
# On your clearnet server (not this onion service)
add_header Onion-Location http://your-address.onion$request_uri always;See the Onion-Location Header Guide for complete implementation details and examples.
- Troubleshooting Guide - Debugging, logs, common issues
- Management Commands - Start, stop, restart, monitor
- Configuration Guide - Customize Tor, Nginx, Docker settings
- Backup Guide - Backup and restore your .onion keys
- Comparison Guide - Compare with OnionSpray and other solutions
- Onion-Location Header Guide - Advertise your .onion on clearnet sites
Vanity addresses are custom .onion addresses with a recognizable prefix (e.g., blockscout...onion).
Important: Vanity address generation can take hours to weeks depending on the desired prefix length. We strongly recommend running the generation process in a terminal multiplexer like tmux or screen to prevent interruption if your SSH session disconnects or your terminal closes.
# Start a tmux session
tmux new -s vanity-gen
# Or use screen
screen -S vanity-gen
# Run your vanity generation command here
# Detach with: Ctrl+b, then d (tmux) or Ctrl+a, then d (screen)
# Reattach with: tmux attach -t vanity-gen (or screen -r vanity-gen)- Memorable: Easier for users to remember and recognize
- Branding: Reinforces your project identity
- Trust: Users can verify they're on the correct service
- SEO: More shareable and professional
Official Tor Project tool: gitlab.torproject.org/tpo/onion-services/onionmine
# Install (requires Rust)
git clone https://gitlab.torproject.org/tpo/onion-services/onionmine.git
cd onionmine
cargo build --release
# Generate address with prefix "block"
./target/release/onionmine --prefix block -n 1
# The more characters, the longer it takes:
# 5 chars: ~30 seconds
# 6 chars: ~15 minutes
# 7 chars: ~8 hours
# 8 chars: ~2 weeks (on fast CPU)High-performance alternative: github.com/cathugger/mkp224o
# Install
git clone https://github.com/cathugger/mkp224o.git
cd mkp224o
./autogen.sh
./configure
make
# Generate with filter
./mkp224o block -d ./keys -n 1
# Use all CPU cores
./mkp224o block -d ./keys -n 1 -t $(nproc)- Use a high-CPU machine: Generation is CPU-bound (cloud instances, dedicated servers)
- Parallel generation: Run multiple instances with different filters
- Cloud burst: Use temporary high-CPU cloud instances (AWS c7i, Azure F-series)
After generation, copy the keys to your setup:
# Keys will be in a directory named with your .onion address
cp keys/your-vanity-address.onion/* tor_data/hidden_service/
chmod 700 tor_data/hidden_service
chmod 600 tor_data/hidden_service/*
# Restart to use new address
docker-compose restart- Censorship resistance via Tor network
- No reliance on potentially malicious exit nodes
- End-to-end encryption through Tor
- Isolated containers for Tor and Nginx
- Server location anonymity (single-hop mode exposes IP)
- DDoS protection (consider Cloudflare → Nginx → Tor for this)
- Rate limiting (add to nginx.conf if needed)
- Backup your keys regularly: See Backup Guide
- Monitor logs for suspicious activity: See Troubleshooting Guide
- Keep containers updated: See Management Guide
- Customize configuration: See Configuration Guide
This setup is perfect for:
- RPC Providers: Expose Ethereum/Bitcoin nodes via Tor (e.g., port 8545)
- Blockchain Explorers: Like this Blockscout example
- Static dApps: Censorship-resistant frontends
- Indexer APIs: GraphQL endpoints, REST APIs
- Development Services: Testing apps over Tor
- Mirror Services: Provide Tor access to existing HTTP services
Contributions are welcome! Please feel free to submit a Pull Request.
For information about the automated testing setup, see CI.md.
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.