Federating the Onions

April 23, 2018 - Reading time: 3 minutes

Within Freedombone it has long been possible to view fediverse instances via an onion address. That has applied to GNU Social, postActiv and more recently Pleroma. But this is really just the client to server part of the communications pipeline and federation between instances (server to server) remained exclusively via the clearnet.

A couple of years ago I did do some investigation of whether I could get GNU Social to federate via onion addresses, which would have the advantage of being independent of the DNS and certificate authority systems. There are a few php Tor proxying examples out there on Github, but none of my experients with federating GNU Social via onion addresses worked out the way I had hoped and I expect that fixing this would require a more involved level of php hacking than I'm currently familiar with.

Recently it has become possible to proxy Pleroma through Tor so that the servers can federate using Tor's DNS resolver, so I've added this as the default behavior both for the ordinary version of Freedombone and also the "onion only" version which, as the name implies, only uses onion addresses to access apps. If you're using Freedombone then this is all automatic, but if you're not the changes needed are quite simple.

If you're using Debian 9.x (the current stable) then you may want to install the tor daemon from backports. This will give you access to the shiny new version 3 onion addresses which have better performance and security properties.

apt-get -yq -t stretch-backports install tor

Create an onion address for your Pleroma instance. Within /etc/tor/torrc:

HiddenServiceDir /var/lib/tor/hidden_service_pleroma/
HiddenServiceVersion 3
HiddenServicePort 80

And restart tor to generate the address:

systemctl restart tor

To find out what the onion address is:

cat /var/lib/tor/hidden_service_pleroma/hostname

Create an nginx configuration for your site. Something like:

proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=100m inactive=80m use_temp_path=off;

server {
    listen default_server;
    server_name yoursiteonionaddress;

    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Permitted-Cross-Domain-Policies none;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;

   access_log /dev/null;
   error_log /dev/null;

   root /etc/pleroma;
   index index.html;

   gzip_vary on;
   gzip_proxied any;
   gzip_comp_level 6;
   gzip_buffers 16 8k;
   gzip_http_version 1.1;
   gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/activity+json application/atom+xml;

   location / {
       client_max_body_size 15m;
       client_body_buffer_size 15m;

       limit_conn conn_limit_per_ip 50;
       limit_req zone=req_limit_per_ip burst=50 nodelay;

       add_header 'Access-Control-Allow-Origin' '*' always;
       add_header 'Access-Control-Allow-Methods' 'POST, GET, OPTIONS' always;
       add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
       if ($request_method = OPTIONS) {
           return 204;

       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       proxy_set_header Host $http_host;
       #proxy_set_header X-Forwarded-Proto $scheme;
       proxy_pass http://localhost:4000;

  location /proxy {
      client_max_body_size 15m;
      client_body_buffer_size 128k;

      limit_conn conn_limit_per_ip 10;
      limit_req zone=req_limit_per_ip burst=10 nodelay;

      proxy_cache pleroma_media_cache;
          proxy_cache_lock on;
          proxy_pass http://localhost:4000;

Where in the above case the Pleroma daemon is running on port 4000.

Now edit your secret.exs Pleroma configuration file and add the following line:

config :pleroma, :http, proxy_url: {:socks5, :localhost, 9050}

You will then need to recompile Pleroma.

cd where_you_installed_pleroma
sudo -u pleroma mix clean
sudo -u pleroma mix deps.compile
sudo -u pleroma mix compile

And restart the pleroma daemon.

systemctl restart pleroma

You should now be able to access Pleroma from the onion address and also federate with other instances which also support server to server onion addresses via a tor proxy.


The blog of Bob Mottram, a Free Software hacker and maintainer of the Freedombone project.

Web site

Email/XMPP: bob@freedombone.net

Matrix: @bob:matrix.freedombone.net