Chrome DevTools Protocol · Reference

CDP on the wire

One-page cheat sheet · discovery, message shapes, core domains

The shape of it

Launch Chrome with --remote-debugging-port=9222 (and a non-default --user-data-dir, required since Chrome 136). Discover targets over plain HTTP, open a target's webSocketDebuggerUrl, then trade JSON: send commands, receive responses and events.src

Message shapes (over the WebSocket)

ShapeHasDirection
Commandid + method (+ params)client → browser
Responseid + result or errorbrowser → client (matched by id)
Eventmethod + params, no idbrowser → client (unsolicited)

Demultiplex by id: has one → a response; none → an event. Responses and events interleave freely; ordering is not ping-pong.

HTTP discovery endpoints (on the debug port)

EndpointGives you
/json/versionBrowser info + the browser-level webSocketDebuggerUrl
/json or /json/listEvery target; each with a type and its own webSocketDebuggerUrl
/json/protocolThe full protocol schema as JSON
/json/new, /json/activate/{id}, /json/close/{id}Create / focus / close a target

Target types include page (a tab — the usual one), service_worker, background_page, browser_ui. Filter for page to drive a tab.

Core domains you'll actually use

DomainFor
PageNavigation, lifecycle events, screenshots, frames
RuntimeEvaluate JS in the page; console API calls
DOMRead/query/modify the DOM tree by nodeId
NetworkWatch requests/responses; read response bodies
InputSynthesize keyboard / mouse / touch
TargetDiscover & attach to targets; sessions
BrowserBrowser-level: version, windows, permissions

Events are opt-in: call Domain.enable (e.g. Page.enable, Network.enable) before you expect that domain's events.

Commands worth memorizing

CommandKey params → result
Browser.getVersion— → product, protocolVersion, userAgent
Page.navigateurl → frameId, loaderId
Runtime.evaluateexpression, returnByValue, awaitPromise → result
DOM.getDocumentdepth, pierce → root node
DOM.querySelectornodeId, selector → nodeId
Page.captureScreenshotformat, quality, clip → data (base64)
Network.getResponseBodyrequestId → body, base64Encoded

Events worth knowing

EventFires when
Page.loadEventFiredThe page's load fired
Page.frameNavigatedA frame committed a navigation
Network.requestWillBeSentA request is about to go out
Network.responseReceivedResponse headers arrived
Runtime.consoleAPICalledPage called console.*

Targets & sessions

Target.attachToTarget {targetId, flatten:true} returns a sessionId. In flat mode you add a top-level sessionId to each command to route it to that target — so one WebSocket can drive many tabs. Target.setAutoAttach {flatten:true} auto-attaches to related targets (iframes, workers). Flat mode is preferred; nested mode is deprecated.docs

Security

The debugging port is unauthenticated — full control to anyone who can reach it. Use a throwaway --user-data-dir, bind to 127.0.0.1, never expose it publicly. Prefer --remote-debugging-pipe (no open port) when you can.chrome