Obscura

Obscura

Documentation Website Book a demo Releases

The open-source headless browser for AI agents and web scraping.
Lightweight, stealthy, and built in Rust.

--- Obscura is a headless browser engine written in Rust, built for web scraping and AI agent automation. It runs real JavaScript via V8, supports the Chrome DevTools Protocol, and acts as a drop-in replacement for headless Chrome with Puppeteer and Playwright. ### Why Obscura over headless Chrome? Designed for automation at scale, not desktop browsing. | Metric | Obscura | Headless Chrome | |--------------|--------------|------------------| | Memory | **30 MB** | 200+ MB | | Binary size | **70 MB** | 300+ MB | | Anti-detect | **Built-in** | None | | Page load | **85 ms** | ~500 ms | | Startup | **Instant** | ~2s | | Puppeteer | **Yes** | Yes | | Playwright | **Yes** | Yes | ## 🎉 10,000 stars and what's next We are working on **Obscura Cloud** the hosted version, with managed infrastructure, residential proxies, and dedicated support. For people who want the engine without operating it themselves. The open-source engine stays Apache-2.0, fully featured. No feature gating, ever. **[Get on the waitlist →](https://tally.so/r/gDWzdD)**
**[📅 Book a demo →](https://cal.com/obscura/quick-chat)** ## Sponsors **Obscura** is supported by sponsors who help keep development independent. Want to sponsor? Email [hello@obscura.sh](mailto:hello@obscura.sh).
SX.org 🚀 Obscura × SX.org
Using Obscura for AI agents, browser automation, or web scraping? Power your workflow with stable proxies from SX.org.

🌍 12M+ IPs across 235 countries
🏠 7M+ residential IPs
📱 4M+ mobile IPs
🏢 1M+ corporate proxies
🔁 Rotating & sticky sessions
📍 Flexible geo setup
🌐 HTTP, HTTPS & SOCKS5 support
⚡ Up to 99.97% connection success
🛟 24/7 support

🎁 Use code Obscura3gb to get a free 3GB trial.

Stable proxies. Fewer blocks. Most reliable Obscura automation.
ProxyEmpire 🚀 Obscura × ProxyEmpire
Using Obscura for AI agents, browser automation, or web scraping? Power it with reliable residential and mobile proxies from ProxyEmpire.

🌍 30M+ residential IPs in 170+ countries
📱 4G/5G mobile proxies
🔄 Rotating & sticky sessions
🎯 City, region & ISP targeting
🔐 HTTP, HTTPS & SOCKS5 support

🎁 Use code OBSCURA35 for a 35% recurring discount.

Better proxies. Fewer blocks. More scalable automation.
MangoProxy MangoProxy provides residential, ISP, datacenter, and mobile proxies in 200+ countries. Trusted by businesses worldwide for stable connections, fast response times, and scalable proxy infrastructure.
Use Promo code OBSCURA for 8% off Static ISP Proxies.
9Proxy 9Proxy provides residential proxies from just $0.018/IP or $0.68/GB. 20M+ IPs across 90+ countries. Sticky or rotating sessions, managed from desktop or mobile app.
NodeMaven NodeMaven — the most reliable proxy provider with the highest quality IPs on the market. Built for automation, web scraping, SEO research, and social media management.

99.9% uptime
Sticky sessions up to 7 days
IP filtering on every proxy
No KYC required
Cashback on traffic — earn up to 10% back

🎁 Use code OBSCURA35 for 35% off Mobile & Residential, or OBSCURA40 for 40% off ISP (Static) proxies.
Rapidproxy Rapidproxy — A residential proxy platform with 90M+ real IPs across 200+ countries. It supports rotation, geo-targeting, and high concurrency to improve scraping success. Start your free trial today!

Flexible pricing starting from $0.65/GB
Traffic that never expires
Supports HTTP / HTTPS / SOCKS5 protocols
High-speed, low-latency network built for scale

🎁 Use discount code RAPID10 to get 10% off.

## Install ### Download Grab the latest binary from [Releases](https://github.com/h4ckf0r0day/obscura/releases): ```bash # Linux x86_64 curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-x86_64-linux.tar.gz tar xzf obscura-x86_64-linux.tar.gz ./obscura fetch https://example.com --eval "document.title" # Linux ARM64 (aarch64) curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-aarch64-linux.tar.gz tar xzf obscura-aarch64-linux.tar.gz # Arch Linux (AUR) yay -S obscura-browser # macOS Apple Silicon curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-aarch64-macos.tar.gz tar xzf obscura-aarch64-macos.tar.gz # macOS Intel curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-x86_64-macos.tar.gz tar xzf obscura-x86_64-macos.tar.gz # Windows Download the `.zip` from the releases page and extract it manually. ``` No Chrome, no Node.js, no dependencies. Release archives include both `obscura` and `obscura-worker`; keep them in the same directory for the parallel `scrape` command. Linux release builds target Ubuntu 22.04 so the downloaded binary remains usable on common LTS servers with glibc 2.35+. ### Docker ```bash docker run -d --name obscura -p 127.0.0.1:9222:9222 h4ckf0r0day/obscura ``` Image on [Docker Hub](https://hub.docker.com/r/h4ckf0r0day/obscura). Multi-stage build on `distroless/cc`, no shell, no package manager, ~57 MB compressed. ### Build from source ```bash git clone https://github.com/h4ckf0r0day/obscura.git cd obscura cargo build --release # With stealth mode (anti-detection + tracker blocking) cargo build --release --features stealth ``` Requires Rust 1.75+ ([rustup.rs](https://rustup.rs)). First build takes ~5 min (V8 compiles from source, cached after). ## Quick Start ### Fetch a page ```bash # Get the page title obscura fetch https://example.com --eval "document.title" # Extract all links obscura fetch https://example.com --dump links # Render JavaScript and dump HTML obscura fetch https://news.ycombinator.com --dump html # Write dump or eval output to a file obscura fetch https://example.com --dump text --output page.txt # Stream the raw response body verbatim (binary-safe; bypasses the JS/DOM layer). # Use this for images, JSON, JS, CSS, or any non-HTML resource. obscura fetch https://picsum.photos/200/300 --dump original > photo.jpg # List every sub-resource URL the page would fetch (NDJSON; one record per asset) obscura fetch https://example.com --dump assets # Fetch through an HTTP or SOCKS proxy obscura --proxy socks5://127.0.0.1:1080 fetch https://example.com --dump text # Wait for dynamic content obscura fetch https://example.com --wait-until networkidle0 # Bound navigation time for slow or broken pages obscura fetch https://example.com --timeout 10 ``` ### Start the CDP server ```bash obscura serve --port 9222 # With stealth mode (anti-detection + tracker blocking) obscura serve --port 9222 --stealth ``` ### Scrape in parallel ```bash obscura scrape url1 url2 url3 ... \ --concurrency 25 \ --eval "document.querySelector('h1').textContent" \ --format json # Suppress scrape progress on stderr for script-friendly output obscura scrape https://example.com --quiet --format json # Scrape workers inherit the global proxy obscura --proxy http://127.0.0.1:8080 scrape https://example.com https://news.ycombinator.com ``` ## Puppeteer / Playwright ### Puppeteer ```bash npm install puppeteer-core ``` ```javascript import puppeteer from 'puppeteer-core'; const browser = await puppeteer.connect({ browserWSEndpoint: 'ws://127.0.0.1:9222/devtools/browser', }); const page = await browser.newPage(); await page.goto('https://news.ycombinator.com'); const stories = await page.evaluate(() => Array.from(document.querySelectorAll('.titleline > a')) .map(a => ({ title: a.textContent, url: a.href })) ); console.log(stories); await browser.disconnect(); ``` ### Playwright ```bash npm install playwright-core ``` ```javascript import { chromium } from 'playwright-core'; const browser = await chromium.connectOverCDP({ endpointURL: 'ws://127.0.0.1:9222', }); const page = await browser.newContext().then(ctx => ctx.newPage()); await page.goto('https://en.wikipedia.org/wiki/Web_scraping'); console.log(await page.title()); await browser.close(); ``` ### Form submission & login ```javascript await page.goto('https://quotes.toscrape.com/login'); await page.evaluate(() => { document.querySelector('#username').value = 'admin'; document.querySelector('#password').value = 'admin'; document.querySelector('form').submit(); }); // Obscura handles the POST, follows the 302 redirect, maintains cookies ``` ## Benchmarks Page load: | Page | Obscura | Chrome | |------|---------|--------| | Static HTML | **51 ms** | ~500 ms | | JS + XHR + fetch | **84 ms** | ~800 ms | | Dynamic scripts | **78 ms** | ~700 ms | The full benchmark suite (WPT conformance, obstacle course, real-world corpus, and vs-Chrome speed) lives in a separate repo: https://github.com/h4ckf0r0day/obscura-benchmark ## Stealth Mode Enable with `--features stealth`. ### Anti-fingerprinting - Per-session fingerprint randomization (GPU, screen, canvas, audio, battery) - Realistic `navigator.userAgentData` (Chrome 145, high-entropy values) - `event.isTrusted = true` for dispatched events - Hidden internal properties (`Object.keys(window)` safe) - Native function masking (`Function.prototype.toString()` → `[native code]`) - `navigator.webdriver = undefined` (matches real Chrome) ### Tracker Blocking - 3,520 domains blocked - Blocks analytics, ads, telemetry, and fingerprinting scripts - Prevents trackers from loading entirely - Enabled automatically with `--stealth` ## CDP API Obscura implements the Chrome DevTools Protocol for Puppeteer/Playwright compatibility. | Domain | Methods | |--------|---------| | **Target** | createTarget, closeTarget, attachToTarget, createBrowserContext, disposeBrowserContext | | **Page** | navigate, getFrameTree, addScriptToEvaluateOnNewDocument, lifecycleEvents | | **Runtime** | evaluate, callFunctionOn, getProperties, addBinding | | **DOM** | getDocument, querySelector, querySelectorAll, getOuterHTML, resolveNode | | **Network** | enable, setCookies, getCookies, setExtraHTTPHeaders, setUserAgentOverride | | **Fetch** | enable, continueRequest, fulfillRequest, failRequest (live interception), takeResponseBodyAsStream | | **IO** | read, close (stream a large response body in chunks) | | **Storage** | getCookies, setCookies, deleteCookies | | **Input** | dispatchMouseEvent, dispatchKeyEvent | | **LP** | getMarkdown (DOM-to-Markdown conversion) | To download a large resource without one giant `Network.getResponseBody` blob, call `Fetch.takeResponseBodyAsStream` then read it in chunks with `IO.read` / `IO.close`. Response bodies over the cache limit (`OBSCURA_NETWORK_BODY_BUFFER_BYTES`, default 2 MiB) are not retained, so raise that limit when you intend to stream large downloads. ## CLI Reference ### Tuning V8 Obscura embeds V8 directly. Use `--v8-flags` to pass raw flags through to V8, same syntax as Chromium's `--js-flags` and Node's command-line flags. Most common use is raising the heap cap to fix `JavaScript heap out of memory` on JS-heavy pages: ```bash obscura --v8-flags "--max-old-space-size=4096" fetch ``` ### Heavy SPAs (script execution budget) Obscura caps the page's script-execution phase so one slow or hung page cannot stall a worker. The default budget is 30s; pages that finish sooner return immediately, so the cap only affects pages that keep running. A very heavy React/Vue/Angular SPA on a slow network can need more time to boot before it fires its data requests. Raise the budget with `OBSCURA_SCRIPT_DEADLINE_MS` (milliseconds), and pair it with a matching navigation timeout in your CDP client: ```bash OBSCURA_SCRIPT_DEADLINE_MS=60000 obscura serve --port 9222 ``` ### `obscura serve` Start a CDP WebSocket server. | Flag | Default | Description | |------|---------|-------------| | `--port` | `9222` | WebSocket port | | `--proxy` | — | HTTP/SOCKS5 proxy URL | | `--stealth` | off | Enable anti-detection + tracker blocking | | `--workers` | `1` | Number of parallel worker processes | | `--obey-robots` | off | Respect robots.txt | ### `obscura fetch ` Fetch and render a single page. | Flag | Default | Description | |------|---------|-------------| | `--dump` | `html` | Output: `html`, `text`, `links`, `markdown`, `assets` (NDJSON of every sub-resource URL the page references), or `original` (raw response body) | | `--eval` | — | JavaScript expression to evaluate | | `--wait-until` | `load` | Wait: `load`, `domcontentloaded`, `networkidle0` | | `--timeout` | `30` | Maximum navigation time in seconds | | `--selector` | — | Wait for CSS selector | | `--stealth` | off | Anti-detection mode | | `--output` | — | Write dump or eval output to a file | | `--quiet` | off | Suppress banner | | `--proxy` | — | Inherited global HTTP/SOCKS5 proxy URL | ### `obscura scrape ` Scrape multiple URLs in parallel with worker processes. | Flag | Default | Description | |------|---------|-------------| | `--concurrency` | `10` | Parallel workers | | `--eval` | — | JS expression per page | | `--format` | `json` | Output: `json` or `text` | | `--quiet` | off | Suppress scrape progress on stderr | | `--proxy` | — | Inherited global HTTP/SOCKS5 proxy URL for all workers | ## MCP (Model Context Protocol) Obscura ships an MCP server that exposes browser automation tools to AI agents (Claude Desktop, Cursor, etc.). ### Start **stdio** (default) — for Claude Desktop and MCP clients that launch a subprocess: ```bash obscura mcp ``` **HTTP** — for clients that connect over the network: ```bash obscura mcp --http --port 8080 # endpoint: http://127.0.0.1:8080/mcp ``` Optional flags (both transports): | Flag | Description | |------|-------------| | `--proxy ` | HTTP/SOCKS5 proxy | | `--user-agent ` | Custom User-Agent string | | `--stealth` | Enable anti-detection mode | ### Claude Desktop config ```json { "mcpServers": { "obscura": { "command": "obscura", "args": ["mcp"] } } } ``` ### Tools | Tool | Description | |------|-------------| | `browser_navigate` | Navigate to a URL (`url`, optional `waitUntil`: `load` / `domcontentloaded` / `networkidle0`) | | `browser_snapshot` | Return the current page URL, title, and body text | | `browser_click` | Click an element by CSS selector | | `browser_fill` | Set an input value (triggers `input` + `change` events) | | `browser_type` | Append text to an input | | `browser_press_key` | Dispatch a keyboard event (`key`, optional `selector`) | | `browser_select_option` | Select an `