Tor is a secure network originally created by the US government in the 1990s [Read More].
The Tor Project [Link] continues the research and development of the “Onion” network and browser.
Assuming you already know what it is and how it works (or have used it before), let’s set up an HTTP hidden service on the Tor network.
We will use Linux (Ubuntu 20.04) for the server. It can also be done on a Raspberry Pi (I will try and share that later).
There are 3 main things needed for this process:
- A web server
- We will set up a simple web server using Python 3, but Apache2 or NGINX is recommended for a more reliable service.
- Note that the web service will only be accessible from localhost (127.0.0.1) because the Tor service also runs on localhost, and the service will be hidden from outside the Tor network.
- Tor Network installed on your server
- Note the difference: Tor Browser is a client that browses inside the Tor Network.
- You will receive a unique .onion address for the service.
With that said, let’s set it up:
sudo mkdir /var/www/tor_service cd /var/www/tor_service python3 -m http.server --bind 127.0.0.1 8080
The last command starts the HTTP server, accessible only at 127.0.0.1 on port 8080, serving the current directory. Feel free to adjust as needed. This command will lock your terminal while the server is running, so leave it open and use a new terminal to continue.
sudo nano /var/www/tor_service/index.html
Add any HTML content, for example:
<html><body>Hello World!</body><html>
Then test your web server:
curl http://127.0.0.1:8080
You should see the plain text of the HTML code.
Now install Tor:
sudo apt install tor -y sudo nano /etc/tor/torrc
If torrc is not there, try searching for it with whereis tor.
Look for the lines below, uncomment them, and adjust to match the configuration shown, or reflect any changes you made to your HTTP server.
HiddenServiceDir /var/lib/tor/hidden_service/ HiddenServicePort 80 127.0.0.1:8080
Start the Tor service and check the address assigned to your server:
sudo tor
If no error message appears, the service is running fine. This terminal will also stay locked while Tor is running, so open a third terminal and retrieve the generated address:
sudo cat /var/lib/tor/hidden_service/hostname
You should see something like:
4l67wy7uuntt6i4tlmdznqjyqq7rwxrtwddcatkvfc6ivclfxtbndeyd.onion
This is your address on the Tor network, think of it as a “domain”.
Note that for this exercise you used 3 terminals:
- First to run the HTTP server;
- Second to run Tor;
- Third to check the hostname (.onion address).
The first and second terminals must keep running to keep the hidden service active. This works well for testing or simulations, but not for a production environment.
For a more permanent setup, run the HTTP server as a service using Apache2 or NGINX.
To configure Tor to run as a daemon, uncomment the following line in /etc/tor/torrc:
RunAsDaemon 1
Also update the following line in /lib/systemd/system/tor.service:
ExecStart=/usr/sbin/tor -f /etc/tor/torrc
Reboot your system and test everything.
To run multiple services on different ports with different .onion addresses, follow this syntax:
HiddenServiceDir /var/lib/tor/hidden_service/ HiddenServicePort 80 127.0.0.1:80 HiddenServicePort 22 127.0.0.1:22 HiddenServiceDir /var/lib/tor/other_hidden_service/ HiddenServicePort 80 127.0.0.1:8080
Feel free to visit the project RPi Zero Tor Hidden Service.
Tip: define the geolocation of the entry and exit nodes.
sudo apt install tor-geoipdb -y sudo nano ~/.local/share/torbrowser/tbb/x86_64/tor-browser_en-US/Browser/TorBrowser/Data/Tor/torrc
Append:
EntryNodes {us} StrictNodes 0
ExitNodes {de},{ca} StrictNodes 1
Country code shortlist:
AUSTRALIA {au}
BELGIUM {be}
BRAZIL {br}
CANADA {ca}
FRANCE {fr}
GERMANY {de}
HONG KONG {hk}
INDIA {in}
RUSSIAN FEDERATION {ru}
SWEDEN {se}
SWITZERLAND {ch}
UNITED KINGDOM {uk}
UNITED STATES {us}
See the full list at [Link].
BONUS
Configure your web server on the clearnet to inform visitors that it also offers an .onion address:

NGINX configuration:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/fullchain.pem;
ssl_certificate_key /etc/ssl/privkey.pem;
add_header Onion-Location http://<your-onion-address>.onion$request_uri;
index index.html;
root /var/www/html;
location / {
try_files $uri $uri/ =404;
}
}
Note: only copy and paste the line in bold into your configuration. The rest of the example is just to show where the additional line should be placed. Don’t forget to restart the web server and verify it worked using a Tor browser.
sudo nginx -t sudo nginx -s reload
APACHE configuration:
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/html
Header set Onion-Location "http://<your-onion-address>.onion%{REQUEST_URI}s"
SSLEngine on
SSLCertificateFile "/etc/ssl/example.com.cert"
SSLCertificateKeyFile "/etc/ssl/example.com.key"
</VirtualHost>
Test your configuration and reload the web server:
sudo apachectl configtest sudo systemctl reload apache2
See also how to Set Up a Tor Node [Link].
See also how to create a Tor Snowflake [Link].