Skip to content

Instantly share code, notes, and snippets.

@DusanMadar
Last active July 24, 2025 20:26
Show Gist options
  • Save DusanMadar/8d11026b7ce0bce6a67f7dd87b999f6b to your computer and use it in GitHub Desktop.
Save DusanMadar/8d11026b7ce0bce6a67f7dd87b999f6b to your computer and use it in GitHub Desktop.
A step-by-step guide how to use Python with Tor and Privoxy

A step-by-step guide how to use Python with Tor and Privoxy

Latest revision: 2025-07-24.

Tested on Ubuntu 24.04 Docker container. The Dockerfile is a single line FROM ubuntu:24.04. Alternatively, you can simply run docker run -it ubuntu:24.04 bash.

NOTE: stopping services didn't work for me for some reason. That's why there is kill $(pidof <service name>) after each failed service <service name> stop to kill it.

References

This guide is basically a compilation of all the resources listed below.

Related

Steps

1. Install and check Tor status

root@75f6721089f2:/# apt update
root@75f6721089f2:/# apt install -y tor
root@75f6721089f2:/# tor --version
Tor version 0.4.8.10.
This build of Tor is covered by the GNU General Public License (https://www.gnu.org/licenses/gpl-3.0.en.html)
Tor is running on Linux with Libevent 2.1.12-stable, OpenSSL 3.0.13, Zlib 1.3, Liblzma 5.4.5, Libzstd 1.5.5 and Glibc 2.39 as libc.
Tor compiled with GCC version 13.2.0
root@75f6721089f2:/# service tor status
 * cannot read PID file /run/tor/tor.pid

2. Start Tor and check it's running

root@75f6721089f2:/# service tor start
 * Starting tor daemon... 
root@75f6721089f2:/# service tor status
 * tor is running

3. Try to Authenticate with nc (Netcat)

It's not possible to connect as ControlPort is not set yet.

root@75f6721089f2:/# apt install -y netcat-traditional
root@75f6721089f2:/# echo -e 'AUTHENTICATE' | nc 127.0.0.1 9051
(UNKNOWN) [127.0.0.1] 9051 (?) : Connection refused

4. Stop/kill Tor, set ControlPort and start Tor again

root@75f6721089f2:/# service tor stop
 * Stopping tor daemon... 
root@75f6721089f2:/# service tor status
* tor is running
root@75f6721089f2:/#  kill $(pidof tor)
root@75f6721089f2:/# service tor status
 * tor is not running
root@75f6721089f2:/# echo "ControlPort 9051" >> /etc/tor/torrc
root@75f6721089f2:/# service tor start 
 * Starting tor daemon... 
root@75f6721089f2:/# service tor status
 * tor is running

5. Try to Authenticate with nc again

It's possible to connect but Authentication fails.

root@75f6721089f2:/# echo -e 'AUTHENTICATE' | nc 127.0.0.1 9051
515 Authentication failed: Wrong length on authentication cookie.

6. Stop/kill Tor, set and check HashedControlPassword then start Tor again

Make sure that you have something like HashedControlPassword 16:ED2893D8EC97801C60DF4A72249CBCCD8B97B3B01A15C923DC49A0E500 (actual password hash can/will differ) in /etc/tor/torrc.

root@75f6721089f2:/# service tor stop
 * Stopping tor daemon... 
root@75f6721089f2:/# service tor status
* tor is running
root@75f6721089f2:/#  kill $(pidof tor)
root@75f6721089f2:/# service tor status
 * tor is not running
root@75f6721089f2:/# echo HashedControlPassword $(tor --hash-password "my password" | tail -n 1) >> /etc/tor/torrc
root@75f6721089f2:/# tail -n 2 /etc/tor/torrc
ControlPort 9051
HashedControlPassword 16:ED2893D8EC97801C60DF4A72249CBCCD8B97B3B01A15C923DC49A0E500
root@75f6721089f2:/# service tor start
 * Starting tor daemon... 
root@75f6721089f2:/# service tor status
 * tor is running

7. Try to Authenticate with nc again

Authentication passes with a correct password.

# NOTE Use Ctrl+C to exit.
root@75f6721089f2:/# echo -e 'AUTHENTICATE' | nc 127.0.0.1 9051
515 Authentication failed: Password did not match HashedControlPassword *or* authentication cookie.
root@75f6721089f2:/# echo -e 'AUTHENTICATE "my password"' | nc 127.0.0.1 9051
250 OK

8. Check your public IP and currently used Tor ip

root@75f6721089f2:/# apt install -y curl
root@75f6721089f2:/# curl https://icanhazip.com/
89.196.159.79
root@75f6721089f2:/# torify curl https://icanhazip.com/
185.220.101.17

9. Change and check Tor IP

root@75f6721089f2:/# echo -e 'AUTHENTICATE "my password"\r\nsignal NEWNYM\r\nQUIT' | nc 127.0.0.1 9051
250 OK
250 OK
250 closing connection
root@75f6721089f2:/# torify curl https://icanhazip.com/
45.90.185.116

10. Change (with Python3) and check Tor IP

root@75f6721089f2:/# apt install -y python3 python3-pip python3-venv
root@75f6721089f2:/# python3 --version
Python 3.12.3
root@75f6721089f2:/# pip3 --version
pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.12)
root@75f6721089f2:/# python3 -m venv my-venv
root@75f6721089f2:/# . my-venv/bin/activate
(my-venv) root@75f6721089f2:/# pip3 install stem==1.8.2
(my-venv) root@75f6721089f2:/# python3
>>> from stem import Signal
>>> from stem.control import Controller
>>> 
>>> with Controller.from_port(port=9051) as controller:
...     controller.authenticate()
...     controller.signal(Signal.NEWNYM)
... 
>>> 
(my-venv) root@75f6721089f2:/# deactivate
root@75f6721089f2:/# torify curl https://icanhazip.com/
192.42.116.182

11. Install privoxy and check traffic is routed through Tor

Now that it's clear Tor is configured and works properly we can include privoxy to the loop.

root@75f6721089f2:/# apt install -y privoxy
root@75f6721089f2:/# privoxy --version
Privoxy version 3.0.34 (https://www.privoxy.org/)
root@75f6721089f2:/# service privoxy status
 * privoxy is not running
root@75f6721089f2:/# echo "forward-socks5t / 127.0.0.1:9050 ." >> /etc/privoxy/config
root@75f6721089f2:/# service privoxy start
 * Starting filtering proxy server privoxy
root@75f6721089f2:/# service privoxy status
 * privoxy is running
root@75f6721089f2:/# torify curl https://icanhazip.com/
192.42.116.182
root@75f6721089f2:/# curl -x 127.0.0.1:8118 https://icanhazip.com/
192.42.116.182

12. Change and check Tor IP with Python3

root@75f6721089f2:/# . my-venv/bin/activate
(my-venv) root@75f6721089f2:/# pip3 install requests==2.32
root@75f6721089f2:/# python3
>>> import requests
>>>
>>> from stem import Signal
>>> from stem.control import Controller
>>>
>>> response = requests.get('https://icanhazip.com/', proxies={'https': 'http://127.0.0.1:8118'})
>>> response.text.strip()
'192.42.116.182'
>>> 
>>> with Controller.from_port(port=9051) as controller:
...     controller.authenticate(password='my password')
...     controller.signal(Signal.NEWNYM)
... 
>>> response = requests.get('https://icanhazip.com/', proxies={'https': 'http://127.0.0.1:8118'})
>>> response.text.strip()
'185.220.101.103'
>>>
>>> response = requests.get('https://api.myip.com/', proxies={'https': 'http://127.0.0.1:8118'})
>>> response.json()
{"ip":"185.220.101.103","country":"Unknown","cc":"XX"}
>>> 

13. [bonus] Change and check Tor IP with TorIpChanger

(my-venv) root@75f6721089f2:/# pip3 install toripchanger==1.3.0
(my-venv) root@75f6721089f2:/# python3
>>> from toripchanger import TorIpChanger
>>> 
>>> tor_ip_changer = TorIpChanger(tor_password='my password', tor_port=9051, local_http_proxy='127.0.0.1:8118')
>>> tor_ip_changer.get_new_ip()
'185.220.101.103'
>>> tor_ip_changer.get_new_ip()
'5.255.118.151'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment