Skip to main content

WebSocket bridge for multi-language Playwright access to Camoufox anti-detect browser

Project description

Camoufox Connector

WebSocket bridge for multi-language Playwright access to Camoufox anti-detect browser

Connect to Camoufox from any programming language that has Playwright bindings - Node.js, Go, Java, .NET, Python, and more.


Sponsored by Scrappey

Scrappey - Web Scraping API

Tired of getting blocked while scraping the web?

Rotating proxies, Anti-Bot technology and headless browsers to CAPTCHAs. It's never been this easy using our simple-to-use API.

๐Ÿ‘‰ Try Scrappey for free


Why Camoufox Connector?

Camoufox is a powerful anti-detect browser based on Firefox, but its Python-only interface limits accessibility. Camoufox Connector solves this by:

  • Exposing WebSocket endpoints that any Playwright client can connect to
  • Managing browser pools for high-volume scraping with fingerprint rotation
  • Providing health monitoring via HTTP API
  • Simplifying deployment with Docker support

Features

  • Multi-language support - Connect from Node.js, Go, Python, Java, .NET, or any Playwright-compatible language
  • Single & Pool modes - One persistent browser or multiple rotating browsers
  • Round-robin load balancing - Distribute connections across browser instances
  • Fingerprint rotation - Each browser instance has a unique fingerprint
  • Health check API - Monitor browser health and statistics
  • Docker ready - Production-ready containerization
  • High performance - Async architecture optimized for concurrent connections

Quick Start

Installation

# Clone the repository
git clone https://github.com/pim97/camoufox-connector.git
cd camoufox-connector

# Install with pip
pip install -e .

# Or install from PyPI (when published)
pip install camoufox-connector

Start the Server

# Single browser mode (default)
camoufox-connector

# Pool mode with 5 browsers
camoufox-connector --mode pool --pool-size 5

# With proxy
camoufox-connector --proxy http://user:pass@host:port

Connect from Node.js

import { firefox } from 'playwright';

// Get endpoint from the connector API
const response = await fetch('http://localhost:8080/next');
const { endpoint } = await response.json();

// Connect to Camoufox
const browser = await firefox.connect(endpoint);
const page = await browser.newPage();

await page.goto('https://example.com');
console.log(await page.title());

await browser.close();

Connect from Go

package main

import (
    "github.com/playwright-community/playwright-go"
)

func main() {
    pw, _ := playwright.Run()
    defer pw.Stop()
    
    // Get endpoint from connector API
    // endpoint := getEndpointFromAPI()
    endpoint := "ws://localhost:9222/abc123"
    
    browser, _ := pw.Firefox.Connect(endpoint)
    page, _ := browser.NewPage()
    
    page.Goto("https://example.com")
    title, _ := page.Title()
    println(title)
    
    browser.Close()
}

Connect from Python

import httpx
from playwright.async_api import async_playwright

async def main():
    # Get endpoint from connector API
    async with httpx.AsyncClient() as client:
        response = await client.get("http://localhost:8080/next")
        endpoint = response.json()["endpoint"]
    
    async with async_playwright() as p:
        browser = await p.firefox.connect(endpoint)
        page = await browser.new_page()
        
        await page.goto("https://example.com")
        print(await page.title())
        
        await browser.close()

Operating Modes

Single Mode (Default)

One browser instance with a consistent fingerprint. Ideal for:

  • Maintaining logged-in sessions
  • Sequential scraping tasks
  • Development and testing
camoufox-connector --mode single

Pool Mode

Multiple browser instances with different fingerprints, distributed via round-robin. Ideal for:

  • High-volume scraping
  • Avoiding detection through fingerprint rotation
  • Parallel processing
camoufox-connector --mode pool --pool-size 5

Note: Since each browser instance maintains its own fingerprint, use pool mode when you need fingerprint rotation between requests. Use single mode when you need session persistence.

HTTP API

The connector exposes an HTTP API for health monitoring and browser management.

Endpoint Method Description
/ GET Server info and version
/health GET Health check (returns 200/503)
/next GET Get next browser endpoint (round-robin)
/endpoints GET List all available endpoints
/stats GET Pool statistics and connection counts
/restart/{n} POST Restart browser instance N

Example API Responses

GET /next

{
  "endpoint": "ws://localhost:9222/abc123def456"
}

GET /health

{
  "status": "healthy",
  "mode": "pool",
  "instances": [
    {"index": 0, "healthy": true, "endpoint": "ws://..."},
    {"index": 1, "healthy": true, "endpoint": "ws://..."},
    {"index": 2, "healthy": true, "endpoint": "ws://..."}
  ]
}

GET /stats

{
  "mode": "pool",
  "total_instances": 3,
  "healthy_instances": 3,
  "active_connections": 5,
  "total_connections": 142,
  "instances": [
    {"index": 0, "uptime": 3600.5, "connections": 2, "total_connections": 48},
    {"index": 1, "uptime": 3600.3, "connections": 2, "total_connections": 47},
    {"index": 2, "uptime": 3600.1, "connections": 1, "total_connections": 47}
  ]
}

Configuration

Command Line Options

Usage: camoufox-connector [OPTIONS]

Options:
  --mode {single,pool}   Operating mode (default: single)
  --pool-size N          Number of browser instances in pool mode (default: 3)
  --api-port PORT        HTTP API port (default: 8080)
  --api-host HOST        HTTP API host (default: 0.0.0.0)
  --ws-port-start PORT   Starting port for WebSocket endpoints (default: 9222)
  --headless             Run browsers in headless mode (default)
  --no-headless          Run browsers in headed mode
  --geoip                Enable GeoIP spoofing (default)
  --no-geoip             Disable GeoIP spoofing
  --humanize             Enable humanization (default)
  --no-humanize          Disable humanization
  --block-images         Block image loading
  --proxy URL            Proxy URL (http://user:pass@host:port)
  --config FILE          Load configuration from JSON file
  --debug                Enable debug logging

Environment Variables

All options can be set via CAMOUFOX_ prefixed environment variables:

export CAMOUFOX_MODE=pool
export CAMOUFOX_POOL_SIZE=5
export CAMOUFOX_HEADLESS=true
export CAMOUFOX_PROXY=http://user:pass@host:port

camoufox-connector

JSON Configuration

{
  "mode": "pool",
  "pool_size": 5,
  "headless": true,
  "geoip": true,
  "humanize": true,
  "proxy": "http://user:pass@host:port"
}
camoufox-connector --config config.json

Docker

Quick Start with Docker

# Build the image
docker build -t camoufox-connector .

# Run in single mode
docker run -p 8080:8080 -p 9222:9222 camoufox-connector

# Run in pool mode
docker run -p 8080:8080 -p 9222-9230:9222-9230 \
  -e CAMOUFOX_MODE=pool \
  -e CAMOUFOX_POOL_SIZE=5 \
  --shm-size=4gb \
  camoufox-connector

Docker Compose

# Single mode
docker compose up

# Pool mode
docker compose --profile pool up

Custom docker-compose.yml

services:
  camoufox:
    build: .
    ports:
      - "8080:8080"
      - "9222-9230:9222-9230"
    environment:
      - CAMOUFOX_MODE=pool
      - CAMOUFOX_POOL_SIZE=5
      - CAMOUFOX_HEADLESS=true
    shm_size: 4gb
    restart: unless-stopped

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     Client Applications                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚  โ”‚ Node.js โ”‚  โ”‚   Go    โ”‚  โ”‚  Java   โ”‚  โ”‚  Other Playwright   โ”‚ โ”‚
โ”‚  โ”‚Playwrightโ”‚  โ”‚Playwrightโ”‚  โ”‚Playwrightโ”‚  โ”‚     Clients        โ”‚ โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ”‚            โ”‚            โ”‚                   โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                              โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚   HTTP API :8080   โ”‚
                    โ”‚  GET /next (RR)    โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                              โ”‚
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚                     โ”‚                     โ”‚
        โ–ผ                     โ–ผ                     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Camoufox 1   โ”‚   โ”‚  Camoufox 2   โ”‚   โ”‚  Camoufox N   โ”‚
โ”‚  WS :9222     โ”‚   โ”‚  WS :9223     โ”‚   โ”‚  WS :922X     โ”‚
โ”‚  Fingerprint Aโ”‚   โ”‚  Fingerprint Bโ”‚   โ”‚  Fingerprint Nโ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Use Cases

High-Volume Web Scraping

// Distribute scraping across multiple fingerprints
async function scrapeUrls(urls) {
  const results = await Promise.all(urls.map(async (url) => {
    // Each request gets a different browser/fingerprint
    const { endpoint } = await fetch('http://localhost:8080/next').then(r => r.json());
    const browser = await firefox.connect(endpoint);
    
    try {
      const page = await browser.newPage();
      await page.goto(url);
      return await page.content();
    } finally {
      await browser.close();
    }
  }));
  
  return results;
}

Session Persistence

// Use a specific endpoint for session persistence
const { endpoints } = await fetch('http://localhost:8080/endpoints').then(r => r.json());
const sessionEndpoint = endpoints[0];  // Always use the same browser

// Login once
let browser = await firefox.connect(sessionEndpoint);
let page = await browser.newPage();
await page.goto('https://example.com/login');
// ... perform login
await browser.close();

// Subsequent requests use the same session
browser = await firefox.connect(sessionEndpoint);
page = await browser.newPage();
await page.goto('https://example.com/dashboard');  // Already logged in

Load Balancing with Health Checks

async function getHealthyEndpoint() {
  const health = await fetch('http://localhost:8080/health').then(r => r.json());
  
  if (health.status !== 'healthy') {
    throw new Error('No healthy browsers available');
  }
  
  const { endpoint } = await fetch('http://localhost:8080/next').then(r => r.json());
  return endpoint;
}

Performance Tips

  1. Use pool mode for parallel tasks - Each browser instance can handle multiple pages concurrently
  2. Set appropriate pool size - Rule of thumb: 1-2 browsers per CPU core
  3. Enable --block-images - Significantly speeds up page loads for text-based scraping
  4. Use --headless - Reduces memory and CPU usage
  5. Monitor with /stats - Watch connection distribution and adjust pool size accordingly

Troubleshooting

Browser fails to start

# Check if Camoufox is installed
python -c "from camoufox.sync_api import Camoufox; print('OK')"

# Install if missing
pip install camoufox
python -m playwright install firefox

Connection refused

# Check if server is running
curl http://localhost:8080/health

# Check if browser WebSocket is accessible
curl -I ws://localhost:9222

Out of memory in Docker

# Increase shared memory (required for browsers)
docker run --shm-size=2gb camoufox-connector

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - see LICENSE for details.

Credits

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

camoufox_connector-1.0.0.tar.gz (19.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

camoufox_connector-1.0.0-py3-none-any.whl (17.2 kB view details)

Uploaded Python 3

File details

Details for the file camoufox_connector-1.0.0.tar.gz.

File metadata

  • Download URL: camoufox_connector-1.0.0.tar.gz
  • Upload date:
  • Size: 19.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for camoufox_connector-1.0.0.tar.gz
Algorithm Hash digest
SHA256 6d18a7395ecd6faa48db988329cf8316f134bb2257adc9aa931fe0f37ec6dfcf
MD5 28e3c1b639b5648e4bbf6f5e37c2de6d
BLAKE2b-256 e41cf148ff7a8c01c2103cdb2e48fe7842b20a3da85e790f2e197e5e5142082c

See more details on using hashes here.

File details

Details for the file camoufox_connector-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for camoufox_connector-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 77453966feee14c07442ce1f5efa3173e5e05bcf107fb2472de457edc39d8791
MD5 2a6237cdc6d315fae0978d342e5190ef
BLAKE2b-256 8a1188f6c0a9f07af1b55f9135ba229c4d7b97c5041717ff541c794b50ec6542

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page