Make your website censorship-resistant using Tor onion services

By Rahul Pandit

Posted on Sunday, 02 August 2020

Last updated on Monday, 07 March 2022

Why Onion Services

Update: I used to host this website and its onion service on my own VPS but in March 2022 I moved the clearnet website to a static site hosting service so I had to take the onion service down.

So, around ten days ago I deployed a Tor onion service for my website and everything's been going swimmingly. I wanted to do it for a long time but Tor project's #MoreOnionsPorFavor challenge provided the perfect kick in the butt. I urge you to take a look at Tor project. They are doing good work developing easy to use, pro-privacy, anti-censorship products. Tor project is run by volunteers so if you like what they are doing consider supporting them.

Onion services are just like regular websites but their domain names are randomly generated and they end in .onion. They can be accessed only using Tor browser. They are end-to-end encrypted by default so a TLS certificate for an onion site is not necessary. Onion services protect your as well as your users' metadata from each other and from any attackers snooping on the network. It means that the IP address of the users visiting an onion site will not be revealed to anybody and the IP address of your server will not be revealed to anybody either.

Furthermore, no government or court can censor your onion service and nor can they block their citizens from accessing your onion service. In other words, your website will become censorship resistant! This is very beneficial to people living in dictatorships or even in democracies with authoritarian governments. So, if you operate a website, I urge you to make it available as an onion service.

By the way, you might be wondering why they chose a name like onion. Actually there is a good analogy behind this. In an onion, you have to peel the outer layers to get to the core. Tor network encrypts your data thrice before it even leaves your device. Then it routes the encrypted data packets through three randomly selected nodes. Each node peels the outer-most encryption layer and forwards the data packet to it's next destination. This way only the intended recipient gets the data in its actual form and no one knows both the sender and the recipient.

Some well-known websites offering onion sites (accessible via Tor browser) :

  • Tor Project :
    http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion
  • DuckDuckGo :
    https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion
  • ProPublica :
    https://p53lf57qovyuvwsc6xnrppyply3vtqm7l6pcobkmyqsiofyeznfu5uqd.onion
  • DW :
    https://www.dwnewsgngmhlplxy6o2twtfgjnrnjxbegbwqx6wnotdhkzt562tszfid.onion
  • BBC :
    https://www.bbcweb3hytmzhn5d532owbu6oqadra5z3ar726vq5kgwwn6aucdccrad.onion
  • The New York Times :
    https://www.nytimesn7cgmftshazwhfgzm37qxb44r64ytbb2dj3x62d2lljsciiyd.onion
  • Protonmail :
    https://protonmailrmez3lotccipshtkleegetolb73fuirgj7r4o4vfu7ozyd.onion
  • SecureDrop :
    http://sdolvtfhatvsysc6l34d65ymdwxcujausv7k5jk4cy5ttzhjoi6fzvyd.onion
  • OnionShare :
    http://lldan5gahapx5k7iafb3s4ikijc4ni7gx5iywdflkba5y2ezyg6sjgyd.onion
  • Facebook (Yes, even Facebook is doing it. Ironic, right?) :
    https://www.facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion

Get Started

First, if you haven't already, download Tor browser on your local machine. Now, on to the server side. I'm working on the assumption that you're using Ubuntu Server 18.04 on a VPS or a bare-metal server. Instructions will differ slightly if you use a different Debian based distro. Another assumption I'm making here is that you're using Nginx in front of your website.

Install all the prerequisites :

sudo apt update
sudo apt install apt-transport-https

Check your distribution's codename. The following command will output bionic if you're using Ubuntu Server 18.04 or focal if you're using Ubuntu Server 20.04 :

lsb_release -c

I got bionic. You may get a different codename. Now, replace codename on the following line with the codename you actually got :

deb http://deb.torproject.org/torproject.org codename main

Since I got bionic as the codename, for me, the line will be :

deb http://deb.torproject.org/torproject.org bionic main

Now copy this line and paste it at the end of the file /etc/apt/sources.list.

Add signing key of Tor developers.

curl https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --import
gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | sudo apt-key add -

Update repositories and install tor.

sudo apt update
sudo apt install tor deb.torproject.org-keyring

Set Up Your Onion Service

We will create a new version 3 onion service and store its details in /var/lib/tor/hidden_service/ directory. The onion service will accept requests on port 80 and forward those requests to localhost:8080. The directory and the ports are configurable and you can change them if you want. But, for now, open /etc/tor/torrc file and append following lines :

HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServicePort 80 127.0.0.1:8080

Restart Tor.

sudo systemctl restart tor

Your onion service should now be running! The two lines that you added to /etc/tor/torrc file above, started the onion service. It created 4 files in /var/lib/tor/hidden_service/ directory :

  • authorized_clients
  • hostname
  • hs_ed25519_public_key
  • hs_ed25519_secret_key

To see the domain name of your onion service, ie, your onion address, check the content of hostname file. You'll get a long random string ending in .onion :

sudo cat /var/lib/tor/hidden_service/hostname

Also, remember to keep your server updated. Security patches and bug fixes are released all the time. You can also enable unattended-upgrades.

Prepare Your Website

Since you already have a website running, you're most likely using a public facing reverse proxy like Nginx, Apache or Caddy in front of your website. It is recommended to use these battle-tested webservers rather than exposing your web application directly to the internet. Now you need to create a new webserver configuration file for your onion service. There are some precautions you need to take before you go ahead. You need to make sure you're using only relative URLs in your existing website, so the internal links to pages within the website won't break when accessed via the onion site. Also you need to make sure hostname and cookie domains are set up in a way that it's possible to access and log in to your website using both clearnet site and onion site.

As an example, I'm giving a sample Nginx configuration file for a static website. Save the file as /etc/nginx/sites-available/myonionsite.conf. Replace youronionaddress below with the onion address you got above :

server {
    # nginx will listen for requests on localhost port 8080
    listen localhost:8080;

    # server_name will be your onion address, ie, the content of /var/lib/tor/hidden_service/hostname file
    server_name youronionaddress;

    # static website is served from /var/www/myonionsite directory
    root /var/www/myonionsite;

    # Some nice-to-have security headers (Optional but highly recommended)
    add_header X-Frame-Options "DENY";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";
    add_header Referrer-Policy "same-origin";
}

For the above Nginx config to work, you'll need to create a new directory /var/www/myonionsite and drop all the HTML and other files needed for the static website. Just for demonstration purposes, create index.html file in /var/www/myonionsite directory.

sudo mkdir /var/www/myonionsite
echo 'hello there!' | sudo tee /var/www/myonionsite/index.html

Enable the configuration :

sudo ln -s /etc/nginx/sites-available/myonionsite.conf /etc/nginx/sites-enabled/

Check whether the config has any errors and, if not, restart nginx.

sudo nginx -t
sudo systemctl restart nginx

Access your website by running curl localhost:8080 on the server. If you see the homepage HTML scroll by, you're good! Otherwise you'll need to troubleshoot the problem. Now access your onion site by running torsocks curl youronionaddress on the server. Replace youronionaddress with your actual onion address, of course.

If everything went without any hitch, congrats! Your onion service is now functional. Open up Tor browser, go visit your onion address and bask in the glory of your new creation!

Take Backup Of The Onion Site

Take a backup of /var/lib/tor/hidden_service/ directory and store it securely. If you later want to change the server, you can just put files in /var/lib/tor/hidden_service/ on the new server in the same directory and add the same 2 lines to /etc/tor/torrc that you added above. Tor will then use the same address rather than generating a new one. Also, take backup of the webserver configuration and the actual website periodically.

Let The World Know

Tell everybody about your onion site. Tell your friends, family and colleagues. Add a little notification on the clearnet version of the website to let every visitor know that you have onion site available.

Add Onion-Location header to let tor visitors of your clearnet website know that you have onion site too. Your clearnet site needs to have HTTPS for Onion-Location header to work. Open webserver configuration of your clearnet website and add Onion-Location header pointing to your onion address. As an example I'll show you how to do it in Nginx. Add following line to the server block listening on port 443 of your clearnet website. Replace youronionaddress with your actual onion address below :

add_header Onion-Location http://youronionaddress$request_uri;

Check whether there are any errors and, if not, restart nginx.

sudo nginx -t
sudo systemctl restart nginx

Now, if you visit the clearnet version of your website in Tor browser, it will show a little button in the address bar telling you that .onion version of the same website is available. And if the visitor clicks on that button, he/she will be taken to the .onion site.

Bonus Section : Vanity Onion URL!

This is completely optional. You can skip this section if you want. You may have noticed that URL of my onion site is not completely random, that it starts with my name rahul. Actually I brute-forced the onion address until I found one starting with rahul. The process of brute-forcing an onion address is not as difficult as you may think. But the time required increases exponentially as the number of characters you want to brute-force increases.

Approximate time required to generate vanity onion URL :

  • Brute-forcing 1 character : <1 second
  • Brute-forcing 2 characters : <1 second
  • Brute-forcing 3 characters : 1 second
  • Brute-forcing 4 characters : 30 seconds
  • Brute-forcing 5 characters : 16 minutes
  • Brute-forcing 6 characters : 8.5 hours
  • Brute-forcing 7 characters : 11.5 days
  • Brute-forcing 8 characters : 1 year

And so on. If you still want to generate a vanity URL for your onion service, read ahead.

First install necessary packages :

sudo apt update
sudo apt install gcc libsodium-dev make autoconf git

On your server, clone mkp224o repo which we will use to brute-force onion addresses.

git clone https://github.com/cathugger/mkp224o

Build the executable from source. Check out optimization tips from mkp224o author.

cd mkp224o
# prepare configuration script
./autogen.sh
./configure --enable-amd64-51-30k
# compile
make

Run the executable. Replace myword below with the string you want to brute-force. If an onion address is found starting with myword, related files will be stored in onion_dir directory. You can Ctrl-C when you find a match or wait for a different one.

./mkp224o -d onion_dir myword

When you're satisfied with the onion address you've brute-forced, copy the files to a new hidden service directory. Replace mywordxyz.onion below with the onion address you just brute-forced.

sudo cp -r onion_dir/mywordxyz.onion /var/lib/tor/new_hidden_service/
sudo chown -R debian-tor: /var/lib/tor/new_hidden_service
sudo chmod -R u+rwx,og-rwx /var/lib/tor/new_hidden_service

Remove the old 2 lines that you added to /etc/tor/torrc file above and add new hidden service details which use your brute-forced onion address.

HiddenServiceDir /var/lib/tor/new_hidden_service/
HiddenServicePort 80 127.0.0.1:8080

Restart Tor.

sudo systemctl restart tor

Open Tor browser and visit the newly minted vanity URL. Enjoy!

Further Reading



Cover Picture Credit : Photo by Wilhelm Gunkel on Unsplash





Recent Posts

Deploy Vaultwarden password manager, Portainer, Nginx and Certbot in Docker


Good Pi-hole blocklists that stop online ads, trackers and malware


Block online ads, trackers and malware with Pi-hole, WireGuard, DoT and DoH servers


Free third-party DNS for blocking ads and trackers


My Chess Notes