Hosting TOR Hidden Services (.onions)

The level of effort required to set up a TOR Hidden Service (known as a .onion) largely relates to the amount of paranoia you need to exercise regarding your anonymity.

Whilst the ins and outs of Operational Security (Op-Sec) are a little too intricate for a single post, this documentation will take you through the steps required to configure a Debian server to host a .onion site with reasonable protections in place.




A Linux server (this documentation is Debian specific)



Once this tutorial is complete, we'll have configured our server to host a .onion, with minimal logging at the OS level. If you're serious about Op-Sec, you should not be connecting to the server from a location that might identify you.

Our server won't appear to be a web-server to the outside world, with HTTP access only available to Tor users.


Installing The Software

Let's begin by installing the software we'll be using

apt-get install nginx
apt-get install ntpdate tor
service tor stop

Note: We install ntpdate because TOR requires an accurate clock


Configuring Hosting

The example below is for a simple static HTML site. If you're planning on using CGI, then it's an exercise left to the reader. Essentially, aside from a few modifications this step isn't that different to configuring standard clearnet hosting (although there are additional things you can do in the name of Op-Sec).

Create the hosting area for our .onion

mkdir -p /usr/share/nginx/onions/myonion
echo "Hello World" > /usr/share/nginx/onions/myonion/index.html
chown -R debian-tor:debian-tor /usr/share/nginx/onions/myonion

We need to adjust the port that NGinx listens on, and configure it with the correct document root

nano /etc/nginx/sites-available/default

# Remove the existing config and replace with
        root /usr/share/nginx/onions/myonion;
        index index.html index.html;

        server_name; # We'll amend this later

        location / {
                autoindex on;


# Save and exit (Ctrl-X, Y)

nano /etc/nginx/nginx.conf

# Locate the http block and add

server_tokens off;

# Save and exit

Once NGinx has been started, you should be able to test the site using wget

service nginx start
wget --header="Host:"


Configure TOR

The next step is to configure the TOR client and tell it to provide a hidden service

cd /etc/tor
mv torrc torrc-default
nano torrc

# Insert the following
SocksPort 0 # what port to open for local application connections
SocksListenAddress # accept connections only from localhost

RunAsDaemon 1
DataDirectory /var/lib/tor

HiddenServiceDir /var/lib/tor/myonion/
HiddenServicePort 80

If we now start TOR, the directory /var/lib/tor/myonion should be created, and two files created within it (including one defining the hostname)

service tor start
cat /var/lib/tor/myonion/hostname

The hostname of your new .onion should be output, so go back and amend the server name directive in the NGinx config file and then reload NGinx

Note: The second file within that directory is private_key. Keep it very, very safe!


Making it all start at boot

Making the services start at boot is as simple as

update-rc.d nginx defaults
update-rc.d tor defaults


Basic Op-Sec Precautions

Obviously, the appropriate Op-Sec precautions will vary by use-case, but at the very least we want to be ensuring the security of the server and reducing the availability of things like bash history.

First lets configure fail2ban.

apt-get install fail2ban
cat << EOM > /etc/fail2ban/jail.local
enabled = true
maxretry = 3
banaction = iptables-allports
enabled = true
maxretry = 3

service fail2ban restart
update-rc.d fail2ban defaults


Disable BASH History

echo 'alias exit="kill -9 $$"' >> /etc/profile
source /etc/profile

You should also consider disabling firewall rules, and it would also be wise to consider ensuring that all egress occurs via TOR to reduce the potential effectiveness of side-channel attacks



Hopefully this documentation has served as a useful introduction to setting up a basic .onion, though the level of op-sec used is very basic so it should be considered nothing more than a primer!

There are a couple of extra points which should also be considered

  • Never configure Tor to both host .onions and be a TOR relay - it allows for easy downtime comparison between your .onion and the publicly available relay information
  • Avoid running multiple .onions on the same server - if downtime occurs (especially if regularly), it may be possible to ascertain that those onions are run (or at least hosted) by the same person

If you're planning on hosting something more complex than a static HTML site, pay close attention to the error messages and headers that it generates - anything containing your IP needs to be removed/disabled/changed.

It's also worth noting that NGinx includes a Client-Peer response header, which appears to contain the IP of the server. However, when the request arrives over TOR, the value of that header will be whatever IP TOR DNS mapped the hidden service to. You can verify this by requesting via TOR with GET:

GET -Sse http://alfk3ti4ld3fq4hj.onion
	GET http://alfk3ti4ld3fq4hj.onion --> 200 OK
	Connection: close
	Date: Sat, 18 Oct 2014 16:05:13 GMT
	Accept-Ranges: bytes
	Server: nginx
	Content-Length: 12
	Content-Type: text/html
	Last-Modified: Sat, 18 Oct 2014 15:53:54 GMT
	Client-Date: Sat, 18 Oct 2014 16:05:41 GMT
	Client-Response-Num: 1

From the beginning though, your primary concern should always be Op-Sec. TOR is a useful tool for anonymity, but it's not a panacea, high-profile takedowns (and arrests) do happen in relation to Hidden Services. For examples, see the take down of Silk Road, Silk Road 2.0, DoxBin and various other examples. If the .onion you're planning to run is anything more than a small personal site, give it serious thought before setting anything live!

Related Reading: