Obscura CDP Compatibility — Zero-Change Migration for Puppeteer and Playwright
Obscura speaks CDP to replace Headless Chrome for Puppeteer and Playwright. 12 CDP domains, lifecycle mapping, known differences, and migration checklist.
16Yun Engineering TeamJul 2, 20262 min read
Why CDP Compatibility Matters
The Chrome DevTools Protocol (CDP) is the underlying protocol Puppeteer and Playwright use to communicate with the browser engine. If you already have scraping and automation scripts written for Puppeteer or Playwright, switching from Headless Chrome to Obscura requires changing only one line of connection code.
Obscura implements 12 CDP domains covering the majority of everyday browser automation needs.
Start the CDP Server
# Basic mode
obscura serve --port 9222
# With anti-detection
obscura serve --port 9222 --stealth
# Docker
docker run -d --name obscura -p 127.0.0.1:9222:9222 h4ckf0r0day/obscura
```bash
The server binds to `127.0.0.1` by default for security. Set `--host 0.0.0.0` for Docker.
## Puppeteer
Use `puppeteer-core` (not `puppeteer`, which bundles Chrome):
```bash
npm install puppeteer-core
```bash
```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://httpbin.org/anything');
const stories = await page.evaluate(() =>
Array.from(document.querySelectorAll('.titleline > a'))
.map(a => ({ title: a.textContent, url: a.href }))
);
console.log(stories);
// Request interception
await page.setRequestInterception(true);
page.on('request', req => {
if (['image', 'media', 'font'].includes(req.resourceType())) {
req.abort();
} else {
req.continue();
}
});
await browser.disconnect();
```bash
## Playwright
Use `playwright-core`:
```bash
npm install playwright-core
```bash
```javascript
import { chromium } from 'playwright-core';
const browser = await chromium.connectOverCDP({
endpointURL: 'ws://127.0.0.1:9222',
});
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://httpbin.org/anything');
console.log(await page.title());
// Form submission
await page.goto('https://httpbin.org/post');
await page.fill('#username', 'admin');
await page.fill('#password', 'admin');
await page.click('form input[type="submit"]');
await browser.close();
```bash
## 12 CDP Domains
| Domain | Key Methods |
|--------|-------------|
| **Target** | createTarget, closeTarget, attachToTarget, createBrowserContext, disposeBrowserContext |
| **Page** | navigate, getFrameTree, addScriptToEvaluateOnNewDocument, lifecycleEvents |
| **DOM** | getDocument, querySelector, querySelectorAll, getOuterHTML, resolveNode |
| **DOMSnapshot** | getSnapshot |
| **Runtime** | evaluate, callFunctionOn, getProperties, addBinding |
| **Network** | enable, setCookies, getCookies, setExtraHTTPHeaders, setUserAgentOverride |
| **Fetch** | enable, continueRequest, fulfillRequest, failRequest |
| **Input** | dispatchMouseEvent, dispatchKeyEvent |
| **Storage** | getCookies, setCookies, deleteCookies |
| **Accessibility** | getFullAXTree |
| **Browser** | getVersion, getWindowBounds, setWindowBounds |
| **LP** (custom) | getMarkdown |
LP is Obscura's custom domain for DOM-to-Markdown conversion.
## Lifecycle Event Mapping
Obscura's lifecycle state machine:
```bash
init → commit → domcontentloaded → load → networkidle2 → networkidle0
```bash
`waitUntil` mapping:
| Puppeteer/Playwright | Obscura |
|----------------------|---------|
| `load` (default) | `load` |
| `domcontentloaded` | `domcontentloaded` |
| `networkidle0` | `networkidle0` |
| `networkidle` | `networkidle0` |
## Known Differences
### 1. Shared V8 Isolate
All pages share one V8 Isolate, serialized through `tokio::sync::Mutex`. `Target.createTarget` is concurrent, but actual navigation runs asynchronously in the background.
### 2. canAccessOpener
Must be present in every `TargetInfo` payload. Strict CDP clients (chromiumoxide) panic without it.
### 3. DevTools Panels
Obscura does not serve Performance, Memory, or other DevTools panels.
### 4. file:// URLs
Disabled by default. Enable with `--allow-file-access`.
## Migration Checklist
1. Replace `puppeteer` with `puppeteer-core`
2. Start `obscura serve` instead of `puppeteer.launch()`
3. Use `puppeteer.connect()` instead of `puppeteer.launch()`
4. Verify you do not depend on Chrome DevTools panels
5. Test request interception, cookies, and evaluate
6. Enable `--stealth` if needed
## Summary
Obscura provides a highly CDP-compatible interface. For most scraping and automation scenarios, migration requires changing only one line of connection code. The extension mechanism allows adding new CDP domains and Web APIs on demand.Need an enterprise proxy plan?
We can tailor architecture to your target domains, concurrency, and reliability goals.