Privacy, Networking, SysAdmin

Hosting a Website on Tor: a Simple Walk-through

In this article, we're going to see how to set up a Tor hidden service (also called onion service), and what are the basic options we can use to configure it. Then, I will explain more about the protocol, and what happens exactly when a client is trying to access a hidden service.

I'll be writing this tutorial based on the latest version of Ubuntu, and assuming there is already a web-server running.


First things first, we need to install Tor. This can be done through apt, but the official documentation advises not to use community repositories, as they have some history of not being properly updated in due time. We will add the repository maintained by Tor in our /etc/apt/sources.list:

deb focal main
deb-src focal main

Then, we will add the repository's PGP key to our system, and install Tor:

curl | gpg --import
gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | apt-key add -
apt install tor


Torrc Configuration File

Now that Tor is installed, we can configure it. This step is unexpectedly easy to do, as we just have to edit the configuration file /etc/tor/torrc to add the following. Note that the service will automatically be created as a v3. You can create a v2 service, but there is no point, as v2 will be disabled soon.

HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServicePort 80

These two lines will do the following:

  • HiddenServiceDir is the place where Tor will create/read the address and keys for the service. You need to make sure that the user debian-tor can properly read/write in it. Also, as it will contain sensitive information, you want to make sure that the access rights are 700.
  • HiddenServicePort allows us to map a port to a service. Here, Tor will send the connections to the port 80 to You can have multiple hidden ports for a single service dir. It is also possible to use a socket to serve the content using unix:/path/to/socket.

Also, note that it is possible to have multiple services configured. You will simply have to repeat the previous two lines with different information (the service directory must be different as well!) while keeping the order HiddenServiceDir, and then HiddenServicePort.

The Hidden Service Dir

When the configuration is edited, and the service relaunched using systemctl restart tor, we should have the following in our hidden_service directory:

Files created in the HiddenServiceDir

hostname will contain the URL for the hidden service, and hs_ed25519_*_key the public and private keys. You will want to carefully backup the keys, as losing them will result in losing your service URL.

The authorized_clients dir allows doing what the name says. To do that, you will need to create files in the format something.auth. Each of these files must only contain one line in the format <auth-type>:<key-type>:<base32-encoded-public-key>. This page is doing a great job explaining how to generate the relevant information to put into this file, and how to configure a client to be able to authenticate.

Adding some Extra Security

I will not get into the details on how to secure a server when using Tor, but here are some - non-exhaustive - list of things you want to do:

  • Make sure not to link any information about the server
  • Beware of access to local resources through Tor
  • Preferably use Tor as a client and not as a relay (to make someone unable to do any correlation between your gateway being down and your hidden service being down as well)
  • Check out the Project Vanguard, which allows adding more security against de-anonymisation attacks. Long story short the scenario Vanguard is trying to avoid is: an adversary makes a lot of requests to your hidden service while running multiple relays. One of its relays might be chosen as a middle node (between the guard and the rendezvous points relays). From there it would be possible to compromise the guard relay and to find the service point's IP.


If for some reason nothing is created in the directory you've put in the configuration, or if you service is unreachable, and if everything is correctly setup, you can start the Tor server using sudo -u debian-tor tor, which will give a bit more information on the server's initialization (for instance if it can't connect to the network).

You can also turn on logging by adding the following to the configuration file:

SafeLogging 0
Log notice file /path/nf.log
Log info file /path/if.log

Note that if you are running Tor in client mode, there is no need to open any incoming port in the firewall. You just need to make sure to allow outgoing connections to the port 443.

How do Clients Connect to the Hidden Service

Now, you might find yourself wondering how everything works connection-wise? This part is there to help you understand that. If after reading you are still hungry for more information, you can have a look at the protocol specifications.

Service Initilization

  1. As we saw in the configuration, we need a public and a private key for the service. They will be used to secure the communications and to generate the service's address. Tor can do it by itself, but some will manually create the key, so they can get a vanity address but repeating the key generation a large number of times until the address begins with a certain string. For example, facebookcorewwwi.onion (see the part three of this article)
  2. When the keys are created, the hidden service will choose a couple of nodes to act as introduction points.
  3. The hidden service then connects to these nodes (using Tor circuits, and never directly as it would defeat the purpose of being anonymous!), and shares its public key with them. It will then keep the connection with them open.
  4. The hidden service then creates a hidden service descriptor containing its public key and who its introduction points are. It will then sign this descriptor with its public key, and upload this to 6 hidden service directories (regular nodes with the HSDir flag enabled) which are part of the DHT (Distributed Hash Table) system.

Note that while the step (1) is obviously only executed at the service creation, the next ones are repeated on a regular basis. If you want to learn more about the DHT, you can have a look at this article (presenting data collection from the DHT).

Clients Connection

When a client tries to connect to a hidden service, the following happens:

  1. The client asks the DHT for the hidden service descriptor
  2. The client chooses a random relay to act as a rendezvous point and connects to it (through a circuit)
  3. The client creates an introduce message, including the address of the rendezvous point, and a one-time secret. It then encrypt it using the hidden service's public key and sends it to an introduction point
  4. The introduction point forwards the introduce message to the hidden service
  5. The hidden service connects to the rendezvous point and sends it the one-time secret in a rendezvous message
  6. The rendezvous point then notifies the client that the connection was successful
  7. From there, the client and hidden service can communicate together. The rendezvous point will act as a gateway, and will only see encrypted messages

Author image

About Ixonae