TOR-PHP is a PHP library for Tor network integration. It provides two main components:
- TorHttpClient - A Symfony HttpClient implementation that routes HTTP requests through the Tor SOCKS5 proxy for anonymous communication.
- TorControlClient - A socket-based client implementing the Tor Control Protocol for managing circuits, configuration, and onion services.
- Quick Start
- Installation
- Requirements
- Authentication
- Features
- Security Baseline
- Usage Examples
- Changelog
- Contributing
- License
-
Install and start Tor:
# Debian/Ubuntu sudo apt install tor sudo systemctl start tor # macOS brew install tor brew services start tor
-
Install TOR-PHP:
composer require ecourty/tor-php
-
Make your first request:
<?php require 'vendor/autoload.php'; use TorPHP\TorHttpClient; $client = new TorHttpClient(); $response = $client->request('GET', 'https://api.ipify.org?format=json'); echo $response->getContent(); // Your Tor exit IP
sudo apt install torbrew install torEdit your Tor configuration file (/etc/tor/torrc or /usr/local/etc/tor/torrc):
ControlPort 9051See the Authentication section for configuring authentication.
composer require ecourty/tor-php- PHP 8.4 or higher
- Tor daemon running locally
- ControlPort enabled in Tor configuration (required for
TorControlClientfeatures)
The Tor ControlPort should always be protected with authentication. TOR-PHP supports three authentication methods.
Recommended for production environments.
-
Generate a hashed password:
tor --hash-password "your_secure_password" -
Add to
/etc/tor/torrc:ControlPort 9051 HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C
-
Use in PHP:
use TorPHP\TorControlClient; $control = new TorControlClient( password: 'your_secure_password' );
Recommended when the PHP process runs as the same user as Tor.
-
Configure
/etc/tor/torrc:ControlPort 9051 CookieAuthentication 1
-
Use in PHP:
use TorPHP\TorControlClient; $control = new TorControlClient( authenticationCookie: '/var/run/tor/control.authcookie' );
The cookie file path varies by system:
- Linux:
/var/run/tor/control.authcookie - macOS (Homebrew):
/usr/local/var/run/tor/control.authcookie
- Linux:
Only use for local development on isolated systems.
Configure /etc/tor/torrc:
ControlPort 9051
CookieAuthentication 0Use in PHP:
use TorPHP\TorControlClient;
$control = new TorControlClient(); // No credentialsWarning: Running Tor ControlPort without authentication is a security risk. Any process on the system can control your Tor instance.
- HTTP/HTTPS requests through Tor SOCKS5 proxy (port 9050)
- Request new identity (change Tor circuit)
- Retrieve current Tor exit nodes
- Compatible with Symfony HttpClient interface
- Circuit management and inspection
- Signal new identity (
NEWNYM) - Configuration management (
GETCONF,SETCONF) - Onion service creation and deletion
- ED25519-V3 keypair generation for onion services
- Tor event subscription
- Wait for circuit build completion
Running Tor ControlPort without authentication is dangerous. An attacker with local access could:
- Reconfigure your Tor instance
- Create onion services
- Monitor your traffic
- Manipulate circuits
Always use password or cookie authentication in production.
- Protect credentials: Never commit passwords or cookie files to version control
- File permissions: Set restrictive permissions on sensitive files:
chmod 600 /path/to/onion/private.key chmod 600 /path/to/.env
- Environment variables: Store credentials in environment variables:
$control = new TorControlClient( password: $_ENV['TOR_CONTROL_PASSWORD'] );
- Sensitive parameter protection: TOR-PHP uses
#[\SensitiveParameter]to prevent credential leakage in stack traces
<?php
use TorPHP\TorHttpClient;
use Symfony\Component\HttpClient\HttpClient;
$torClient = new TorHttpClient();
$response = $torClient->request('GET', 'https://api.ipify.org?format=json');
$torIp = $response->toArray()['ip'];
$normalClient = HttpClient::create();
$normalIp = $normalClient->request('GET', 'https://api.ipify.org?format=json')->toArray()['ip'];
echo "Tor IP: $torIp" . PHP_EOL;
echo "Non-Tor IP: $normalIp" . PHP_EOL;<?php
use TorPHP\TorHttpClient;
$torClient = new TorHttpClient();
$torClient->request('GET', 'https://example.com');
// Change circuit when you need
$torClient->newIdentity();
$response = $torClient->request('GET', 'https://example.com/another-page');<?php
use TorPHP\TorControlClient;
use TorPHP\Model\PortMapping;
$control = new TorControlClient();
// Get circuits
$circuits = $control->getCircuits();
// Change config
$control->setConfigValue('SocksPort', '9080');
// Add onion service
$onion = $control->addOnionService([
new PortMapping(host: 'localhost', localPort: 3000, remotePort: 80),
]);
// List onion services
$services = $control->listOnionServices();
// Delete onion service
$control->deleteOnionService($onion->id);<?php
use TorPHP\Helper\KeyPairGenerator;
use TorPHP\TorControlClient;
use TorPHP\Model\PortMapping;
// Generate a new ED25519-V3 keypair
$keyPair = KeyPairGenerator::generate();
echo "Service ID: {$keyPair->serviceId}.onion\n";
echo "Private Key: {$keyPair->getPrivateKeyFormatted()}\n";
// Use the generated key to create an onion service
$control = new TorControlClient();
$onion = $control->addOnionService(
portMappings: [new PortMapping('127.0.0.1', 8080, 80)],
privateKey: $keyPair->getPrivateKeyBase64()
);
// The onion service ID will match the derived serviceId from the keypair
assert($onion->id === $keyPair->serviceId);Other code examples can be found here.
See CHANGELOG.md for details.
See CONTRIBUTING.md for contribution guidelines.
Author: Edouard Courty
License: MIT - See LICENSE
Copyright © 2025
Feel free to open an issue or contribute via pull requests.