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.