Many software projects now use TOR hidden services to bypass local NAT firewalls or to make their self-hosted, at-home services available remotely without a static, public IP address. There are also privacy benefits for both service operators and users.

This article assumes that you already have the TOR hidden service properly configured and everything is running smoothly on the service side. So how do you connect to a TOR hidden service remotely using nodejs? It's actually pretty simple. We will use TOR's socks proxy, a socks proxy agent module, and the built-in http/s modules of nodejs.

Install TOR

First you will need to install TOR. On debian/Ubuntu you can use the following command:

sudo apt-get install tor

This should automatically setup a service to run the TOR proxy on system boot.

For other systems have a look at the official website for installation guides.

Verify that the TOR proxy is working:

curl --verbose \
    --socks5 127.0.0.1:9050 \
    --socks5-hostname 127.0.0.1:9050 \
    -s https://check.torproject.org/ | cat | grep -m 1 Congratulations | xargs

If your TOR proxy is working then you should see "Congratulations" printed to your terminal.

Nodejs script to fetch web page from TOR hidden service

Below is an example script that can fetch a web page served via a TOR hidden service:

const url = require('url');
const http = require('http');
const https = require('https');
const SocksProxyAgent = require('socks-proxy-agent');

// Use the SOCKS_PROXY env var if using a custom bind address or port for your TOR proxy:
const proxy = process.env.SOCKS_PROXY || 'socks5h://127.0.0.1:9050';
console.log('Using proxy server %j', proxy);
// The default HTTP endpoint here is DuckDuckGo's v3 onion address:
const endpoint = process.argv[2] || 'https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion';
console.log('Attempting to GET %j', endpoint);
// Prepare options for the http/s module by parsing the endpoint URL:
let options = url.parse(endpoint);
const agent = new SocksProxyAgent(proxy);
// Here we pass the socks proxy agent to the http/s module:
options.agent = agent;
// Depending on the endpoint's protocol, we use http or https module:
const httpOrHttps = options.protocol === 'https:' ? https : http;
// Make an HTTP GET request:
httpOrHttps.get(options, res => {
    // Print headers on response:
    console.log('Response received', res.headers);
    // Pipe response body to output stream:
    res.pipe(process.stdout);
});

Create a new directory:

mkdir ./tor-http

Create a new file:

touch ./tor-http/fetch.js

Copy/paste the above script into the new file.

Run the script as follows:

node ./tor-http/fetch.js

By default it will fetch DuckDuckGo's home page via its v3 onion address. You can use the script to fetch another page instead:

node ./tor-http/fetch.js "http://your-tor-hidden-service.onion"

The script will work with both "http" and "https" URLs.

If you need to send custom headers or otherwise change the HTTP request to better suit your needs, have a look at the nodejs http module docs.