Setting up a Tor hidden service with nginx

Quick guide based on @CthulhuSec's tutorial for setting up a Tor hidden service. If you don't know what a hidden service is read this high-level description before continuing.

Install Tor

Install the Tor client, make sure you are running a recent version.

emerge -av net-misc/tor

If you want to automatically start Tor at boot just add the service with the default run level: rc-update add tor default. If you want to start the service right away just use rc-service tor start.


In this step we are going to add our hidden service to the Tor configuration file called torrc located in /etc/tor/torrc. It's possible to run multiple hidden services, and it's not a problem to service the same hidden service from multiple ports. In that case just duplicate the HiddenServiceDir and HiddenServicePort lines like that:

HiddenServiceDir /usr/local/etc/tor/hidden_service/
HiddenServicePort 80

HiddenServiceDir /usr/local/etc/tor/other_hidden_service/
HiddenServicePort 6667
HiddenServicePort 22

We are only going to add one service for now.

Open the configuration file using a text editor (vim /etc/tor/torrc) and add your hidden service:

# Minimal torrc so tor will work out of the box
User tor
PIDFile /var/run/tor/
Log notice syslog
DataDirectory /var/lib/tor/data

HiddenServiceDir /var/lib/tor/data/
HiddenServicePort 80

Reload your Tor service after that.

/etc/init.d/tor reload

Once you do that the new hostnames and the private keys for your hidden services will be created and are now located in your DataDirectory. In my case it'll look like this:

root@notmyhostname /var/lib/tor$ tree
└── data
    ├── cached-certs
    ├── cached-microdesc-consensus
    ├── cached-microdescs
    ├── hidden_service
    │   ├── hostname
    │   └── private_key
    │   ├── hostname
    │   └── private_key
    │   ├── hostname
    │   └── private_key
    ├── lock
    └── state

4 directories, 12 files

Important: First, Tor will generate a new public/private keypair for your hidden service. The file called private_key contains your private key, make sure you don't share that with others. They'll be able to impersonate your hidden service.

The file hostname will contain the .onion URL for your new hidden service (That's the beforementioned public key, or to be specific: The hash of the public key). In my case that's j5wfzhvvrf2dwm2v.onion. Write that down somewhere because you'll need that in the next step.

Configure the web server (nginx)

Create a new configuration file for the service you want to serve. In my case I'll call it just so it's easier to differentiate between that and the config file for the non-Tor site.

vim /etc/nginx/sites-available/ could look like this:

server {
    server_name cc7yjqvfpwtl6uhy.onion;

    error_log   /var/log/nginx/;
    access_log  off;

    location / {
        root /var/www/;
        index index.html;

Make sure to replace the server_name with the hostname from the earlier step.

Now just enable that new configuration by symlinking it to the sites-available directory and reload nginx:

ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/
/etc/init.d/nginx reload

To test it just download or start TorBrowser and try to access your personal .onion URL. Unfortunately serving a hidden service over https is not yet possible (There are exceptions...). If you want more information about that read this blog post by the Tor developers.

That's all. Easy!