NEWNYM is the Tor control-port signal that tells the daemon "stop using current circuits for new streams, build fresh ones." It rotates the middle and exit relays of your circuit without disturbing in-flight streams, which is exactly what you want when scraping behind Tor and you've burned through a rate limit. The setup is one torrc block and a few lines of code — the surprise is the silent 10-second rate limit Tor enforces server-side. Below is the working torrc, a raw-socket one-liner, a stem script, and the gotcha list.
Step 1: open the control port in torrc
NEWNYM goes through the Tor control port, default 9051. Open it and pick an auth method.
# /etc/tor/torrc
ControlPort 9051
# Option A: cookie auth (preferred locally)
CookieAuthentication 1
CookieAuthFileGroupReadable 1
# Option B: password auth (use if controller runs elsewhere)
# HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4CCookie auth is cleaner on the same host because filesystem permissions handle who can read the cookie. Password auth is the right answer when the controller process is a different user (or a different machine over SSH-tunnel).
Generate the password hash with Tor itself:
tor --hash-password 'yourpassword'
# 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4CPaste the output (the whole 16:... string) into HashedControlPassword. Reload:
sudo systemctl reload tor
# or
sudo pkill -HUP torStep 2: send NEWNYM
Three working approaches, in increasing order of cleanliness.
Raw socket (no dependencies, copy-paste)
If you have HashedControlPassword set, the password is sent in plaintext over the local socket (it's localhost — that's fine):
{
echo "AUTHENTICATE \"yourpassword\""
echo "SIGNAL NEWNYM"
echo "QUIT"
} | nc 127.0.0.1 9051Expected output:
250 OK
250 OK
250 closing connection
Three 250s mean: authenticated, signal accepted, clean disconnect. Anything else is a problem.
For cookie auth, read the cookie file and pass it hex-encoded:
COOKIE=$(xxd -c 256 -p /var/lib/tor/control_auth_cookie)
{
echo "AUTHENTICATE $COOKIE"
echo "SIGNAL NEWNYM"
echo "QUIT"
} | nc 127.0.0.1 9051The cookie file path depends on your DataDirectory. On most Debian/Ubuntu installs it's /var/lib/tor/control_auth_cookie. On macOS Homebrew it's /opt/homebrew/var/lib/tor/control_auth_cookie (Apple Silicon).
Python with stem
stem is the official Python controller for Tor. Cleaner error messages, type-safe signal names, and it handles cookie auth transparently:
pip install stemfrom stem import Signal
from stem.control import Controller
with Controller.from_port(port=9051) as controller:
controller.authenticate() # auto-detects cookie or env-provided password
controller.signal(Signal.NEWNYM)
print("New circuit signalled.")If you're using password auth instead of cookie:
with Controller.from_port(port=9051) as controller:
controller.authenticate(password="yourpassword")
controller.signal(Signal.NEWNYM)Use a Unix socket if you have ControlSocket set in torrc — same API, swap from_port for from_socket_file:
with Controller.from_socket_file("/var/run/tor/control") as controller:
controller.authenticate()
controller.signal(Signal.NEWNYM)nyx (interactive)
nyx is the terminal Tor monitor (formerly arm). Run it, press m, choose "new identity" — it sends NEWNYM under the hood. Useful when you want to watch the circuit list update in real time. Install via apt install nyx on Debian/Ubuntu or brew install nyx on macOS.
Step 3: verify the circuit actually rotated
NEWNYM is fire-and-forget. Tor returns 250 OK regardless of whether new circuits build successfully. To prove it worked, check the exit IP before and after:
# Before
torsocks curl https://check.torproject.org/api/ip
# {"IsTor":true,"IP":"185.X.X.X"}
# Send NEWNYM
echo -e "AUTHENTICATE \"yourpass\"\nSIGNAL NEWNYM\nQUIT" | nc 127.0.0.1 9051
# Wait at least 10 seconds (see rate limit below), then check
sleep 12
torsocks curl https://check.torproject.org/api/ip
# {"IsTor":true,"IP":"94.X.X.X"}A different IP confirms the circuit rotated. Sometimes the new circuit picks the same exit by chance — try a couple of NEWNYMs to be sure.
The 10-second rate limit
This is what every NEWNYM tutorial omits. Tor enforces a minimum interval of MaxClientCircuitsPending * server-side guard, which in practice means at most one NEWNYM every ~10 seconds is honored by the relay set. Fire ten in a tight loop, and most are dropped silently.
If you need rotation faster than every ten seconds, run multiple Tor instances on different control and SOCKS ports, then round-robin NEWNYM across them:
# /etc/tor/torrc-a
SocksPort 9050
ControlPort 9051
CookieAuthentication 1
DataDirectory /var/lib/tor-a
# /etc/tor/torrc-b
SocksPort 9060
ControlPort 9061
CookieAuthentication 1
DataDirectory /var/lib/tor-bStart both:
tor -f /etc/tor/torrc-a &
tor -f /etc/tor/torrc-b &Now you have two independent SOCKS proxies on 9050 and 9060, each with its own control port. Alternate which one your scraper hits per request, and rotate each on its own ten-second cadence. The effective rotation rate doubles, triples, scales.
What NEWNYM does and doesn't do
NEWNYM:
- Marks all current circuits as dirty so new streams open on fresh circuits.
- Builds new circuits with new middle and exit relays.
- Returns immediately; circuit construction happens in the background.
NEWNYM does not:
- Change your entry guard. Tor uses long-lived guards on purpose — rotating them on every NEWNYM would defeat their security properties. To force a new guard, you have to delete your
statefile in theDataDirectoryand restart, which is slow and rarely what you actually want. - Close existing streams. Any TCP connection currently open keeps its circuit until the application closes it.
- Affect hidden-service circuits. Those rotate on their own schedule.
If you genuinely need a new guard (compromise suspected, threat-model change), the correct fix is rotating the guard fingerprint in state, not abusing NEWNYM.
Per-stream circuit isolation: a different lever
Sometimes you don't want to wait ten seconds — you want a different circuit right now for one specific request. NEWNYM isn't the tool; stream isolation is. Set SocksPort to isolate by something:
SocksPort 9050 IsolateDestAddr IsolateDestPort IsolateSOCKSAuthIsolateSOCKSAuth is the magic one: any unique SOCKS username/password pair gets its own circuit. So in Python:
import requests
def fetch(url, isolation_tag):
proxies = {
"http": f"socks5h://{isolation_tag}:x@127.0.0.1:9050",
"https": f"socks5h://{isolation_tag}:x@127.0.0.1:9050",
}
return requests.get(url, proxies=proxies, timeout=30)
# Each unique isolation_tag gets a separate circuit
r1 = fetch("https://check.torproject.org/api/ip", "request-1")
r2 = fetch("https://check.torproject.org/api/ip", "request-2")
print(r1.json()["IP"], r2.json()["IP"])
# Two different exit IPsThis is the right answer for high-throughput scraping. NEWNYM is the right answer when you want to start over with a fresh circuit pool for everything.





