Cloudflare released Kitesurf as open source under the Apache 2.0 license this week, and the decision it creates for agent teams is narrower than the launch framing suggests. The question is not whether to replace your headless Chromium containers. It is which workloads move to a browser engine that runs on your own Workers account, and which stay in Docker. Based on Cloudflare’s own documentation, the split is clean: agents that load pages, extract HTML, and screenshot JavaScript-light sites are candidates for Kitesurf, while anything needing full V8 execution, video or WebGL, real TLS fingerprints, or a long authenticated session stays in containerized Chromium.
The tradeoffs sit in the vendor’s own benchmark table, published 2026-11-13, and the only published numbers are Cloudflare’s own. Kitesurf runs 1.8× slower than Chromium on screenshot wall time (1,148 ms versus 637 ms), per Cloudflare’s Browser Run documentation, and 1.7× slower on HTML extraction (820 ms versus 472 ms). The same table reports Kitesurf using 3.1× to 3.8× less CPU and 4.7× to 7.0× less memory than Chromium, so the launch’s efficiency promise now has measurement behind it, not just a goal statement. What you are actually buying today is a different operational model: no container fleet, per-request execution on Workers, and a JavaScript compatibility surface you have to verify site by site.
What self-hosting Kitesurf actually means
The open-source announcement is explicit about the deal: “Kitesurf is open source on GitHub under the Apache 2.0 license. You can read the code, run it locally, change it, and deploy your own instance to Cloudflare Workers.” The project site states the architectural point plainly: “There is no Chromium process or container behind it.”
Instead, the engine is assembled from an ecosystem of Rust packages compiled to WebAssembly. Per the announcement, Blitz provides HTML parsing, the DOM, layout, and painting; Stylo handles CSS; Linebender libraries cover text layout and 2D graphics; Krilla generates PDF output. The repository has three main parts: the Engine manages browser sessions and client connections, PageScript runs the page and its JavaScript, and PageRenderer draws the result. That split matters operationally, because it tells you where a failure will live: a site that renders wrong is a Blitz or Stylo problem, a site whose scripts misbehave is a PageScript problem, and a client that cannot drive the session is an Engine problem.
The local development loop is ordinary Workers tooling: clone the repository, pnpm install --frozen-lockfile, pnpm run dev, and wrangler serves a local engine at http://127.0.0.1:8787, which you can open in Chrome DevTools or drive programmatically with puppeteer-core over a websocket endpoint. When you are satisfied, pnpm run deploy pushes the engine to your own Cloudflare account, where it runs instead of Cloudflare’s managed Browser Run APIs.
One entry condition is not optional: “Note that a Workers Paid plan is required because Kitesurf uses Dynamic Workers to create isolated Workers for pages.” Self-hosting here does not mean hosting on your own hardware. It means your Workers account, your deployment, your bill, and Cloudflare’s platform limits, whatever those turn out to be for your session shapes. Neither the open-source announcement nor the Browser Run documentation states which Workers CPU, memory, or duration limits apply to a self-hosted Kitesurf session, so check Cloudflare’s general Workers limits, which govern any Worker deployment, against your expected session shapes. The session cap itself remains an open verification item rather than a number I can give you.
The incumbent you would be replacing
The Docker-Chromium stack is the established substrate for a reason. Puppeteer is “a JavaScript library which provides a high-level API to control Chrome or Firefox over the DevTools Protocol or WebDriver BiDi,” and Playwright layers auto-waiting, assertions, tracing, and parallelism across Chromium, Firefox, and WebKit, with Playwright MCP giving AI agents browser control through structured accessibility snapshots. A container fleet running one of these clients gives you the full V8 engine, the complete CDP surface, real TLS fingerprints from a real network stack, and sessions that persist as long as you keep the container alive. It also gives you the fleet: images to rebuild, Chromium versions to pin, memory ceilings to tune, and orchestration to babysit.
Kitesurf’s pitch is that a large class of agent work, the read-mostly fetch-render-extract loop, does not need any of that. That pitch is plausible. It is not yet proven outside Cloudflare’s own materials.
CDP is the hinge, and the docs do not state its breadth
Everything about migrating an existing agent harness to Kitesurf hangs on the Chrome DevTools Protocol. CDP is the de facto control plane for browser automation: independent academic work describes it as “widely used for running End-to-End (E2E) tests on web-based applications through popular tools like Selenium, Puppeteer and Playwright, and for building crawlers” (arXiv:2305.11506). Because the tooling ecosystem standardised on CDP, a new engine that speaks it can slot into existing harnesses, and a new engine that speaks only part of it will fail in ways you discover mid-run.
Kitesurf supports CDP, so Puppeteer-class clients can connect, and Cloudflare’s managed Browser Run endpoint accepts a browser=kitesurf option that lets existing Puppeteer, Playwright, or chrome-remote-interface setups opt in without code changes. That is what works today, and it is all the evidence there is. Cloudflare documents Playwright connections through that managed CDP endpoint, while the local self-hosting instructions in the announcement use puppeteer-core. The announcement and the Browser Run documentation do not state how much of CDP a self-hosted instance implements relative to what a full Playwright session expects, and the repository opened on 2026-11-13, so any issue history to mine for the gap list is days old at most.
The pre-migration step follows directly: enumerate the CDP domains and methods your harness actually calls, then exercise each one against a locally running Kitesurf engine before deploying anything. A screenshot-and-extract harness touches a small slice of the protocol. A harness that intercepts network requests, synthesises input events, or drives complex frame trees touches a much larger one. Which slice you need determines how much diligence this step costs you.
Where your page JavaScript actually runs
The deepest difference between the two substrates is not containers versus Workers. It is what executes the page’s scripts. In Chromium, one mature engine, V8, runs everything. In Kitesurf, page JavaScript runs inside Dynamic Workers, isolated per page, and “Boa runs JavaScript inside WebAssembly (as the last resort only when we can’t run JavaScript in Dynamic Workers),” according to the announcement.
That fallback path is the migration risk in one sentence. Two execution environments with different fidelity means a site can render differently depending on which path its scripts take, and the announcement and Browser Run documentation offer standards-suite coverage rather than any failure-rate data on production sites. Kitesurf passes over 235,000 Web Platform Tests subtests, per the Browser Run docs, with 97% subtest coverage for DOM and 96% for HTML as of the latest run. Those are respectable numbers for a young engine, and they are also the wrong unit for your decision. WPT subtests measure standards conformance; your agents care whether the twenty sites they hit every hour produce usable DOM and screenshots. The only way to close that gap is to run your own target list through both substrates.
The benchmarks that exist, read honestly
The published record is one table, run by Cloudflare: six measured metrics, drawn from medians of five Quick Action runs across a 14-URL corpus on the managed Browser Run service, with Chromium drawn from a warm pool.
| Measure (Cloudflare-reported) | Kitesurf | Chromium | Delta |
|---|---|---|---|
| Wall time: screenshot | 1,148 ms | 637 ms | 1.8× slower |
| Wall time: HTML extraction | 820 ms | 472 ms | 1.7× slower |
| CPU: screenshot | 380 ms | 1,173 ms | 3.1× less |
| CPU: HTML extraction | 229 ms | 877 ms | 3.8× less |
| Memory: screenshot | 57.8 MiB | 271.0 MiB | 4.7× less |
| Memory: HTML extraction | 39.4 MiB | 273.7 MiB | 7.0× less |
The launch framing (“Our main goal is to give AI agents a lightweight, efficient browser — one that can load websites, execute JavaScript, and interact with pages while using less CPU and memory than a full browser like Chromium”) is now backed by measurement on the two agentic tasks tested. Cloudflare’s own summary of the table: “Kitesurf wins on the memory and CPU that drive your bill (by 3–7×), while Chromium wins wall time because a warm just-in-time compiler beats a cold software renderer.” For read-mostly agent work, that trade favors Kitesurf when sessions are fanned out in parallel and per-session resource cost dominates, and favors Chromium when a caller blocks waiting on each screenshot.
Three conditions keep this from being a verdict. The runs executed on Cloudflare’s managed Browser Run, not on a self-hosted deployment billed to your Workers account. No independent replication accompanies the published table. And no Docker Chromium fleet baseline accompanies the comparison, so the table cannot settle what a migration saves or costs you. Treat the 1.7–1.8× wall-time figures as the latency price and the 3–7× resource figures as a vendor-reported direction to confirm against your own bill.
What stays in Docker, by Cloudflare’s own admission
The documentation is unusually direct about scope: “Kitesurf is not yet the right option if you need to: Play video or render WebGL. Negotiate a bot-challenge handshake with real TLS fingerprints. Start a long-running, authenticated session that requires persistent state.”
Each exclusion maps to a real agent workload. Video and WebGL cover anything that scrapes rendered media. Canvas is a different story: it is not on the exclusion list, and the project playground demonstrates 2D canvas working, including a WebAssembly port of Doom drawn to a canvas, Paper.js paths and hit testing, and the tldraw infinite-canvas whiteboard. Treat canvas output as a verify-first item on your targets rather than a hard exclusion. TLS fingerprints cover any target fronted by bot mitigation that inspects the client’s handshake; the documented exclusion is specific to bot-challenge handshakes and is framed as “not yet the right option”, not as a permanent property of the network stack. Long authenticated sessions cover logged-in workflows, and here the project’s own description does the explaining: Kitesurf is a stateless browser. Persistent login state across a multi-step session is exactly what the architecture declines to provide.
For these workloads the answer is boring and correct: keep running Chromium in Docker under Playwright or Puppeteer. If your agents rely on Playwright MCP’s accessibility snapshots, that path assumes a real browser behind the MCP server, which today means Chromium, Firefox, or WebKit.
The detection question cuts both ways
The TLS fingerprint exclusion sounds disqualifying for any agent that touches defended sites, and for targets that gate on handshake inspection, it is. But fingerprinting is only one detection layer, and the behavioral layer is leakier than the security marketing suggests. In a study of behavioral detection under browser automation, an MLP classifier let 39.1% of automated agents pass as human. The same study found a SAINT tabular transformer let 34.5% through, and a random forest 30.0%. The study’s one complete detector, an XGBoost model, caught 100% of agents (arXiv:2607.26935), but it did so “via coincidental features (missing scroll events, absent raw mousemove streams before clicks, empty wheel-delta series) rather than agent-specific signals, so its detection is brittle.” Detection outcomes that uneven mean the fingerprint gap between Kitesurf and Chromium will often not be the deciding factor in whether your agent is flagged.
So weight this axis honestly. If your targets run TLS-fingerprint-based challenges, Kitesurf is out for those targets, and no behavioral argument changes that. If they do not, the detection difference between substrates is smaller than it looks, and you should not let it dominate a decision that is really about rendering fidelity and operations.
The cost model nobody has priced
You will see claims, soon if not already, that self-hosted Kitesurf is cheaper than a Chromium fleet. The honest position is that the published numbers do not settle it. Cloudflare’s table reports Kitesurf using 3–7× less CPU and memory than Chromium, but those runs executed on managed Browser Run, not on a self-hosted engine billed to a Workers account, and no Workers pricing analysis or container fleet baseline accompanies the published table. Any break-even calculation you build today is a public-data estimate whose assumptions, your request volume, your session durations, your fleet utilisation, you must expose and supply yourself.
What can be said structurally: the cost shape changes. A container fleet bills for idle capacity and engineering time; per-request Workers execution bills for what runs and shifts the ceiling questions onto platform limits that neither the announcement nor the Browser Run documentation addresses for this use. I would treat claimed savings as a hypothesis to test during the pilot, with your own bill as the instrument, rather than a reason to migrate.
Pick the substrate per workload
| Agent workload | Substrate | Why | Verify first |
|---|---|---|---|
| High-volume page loads, HTML extraction, screenshots of JS-light sites | Self-hosted Kitesurf | No container fleet; per-request Workers execution; CDP clients connect | Render success and wall time on your top target sites |
| JavaScript-heavy apps where DOM fidelity matters | Docker Chromium | Full V8; Kitesurf falls back to Boa-in-WebAssembly when Dynamic Workers cannot run a script | Diff extracted DOM between substrates on your targets |
| Video playback and WebGL rendering | Docker Chromium | Explicitly excluded in Cloudflare’s docs | None needed |
| 2D canvas rendering and interaction | Either, verify first | Not excluded; the playground demos Doom, Paper.js and tldraw on canvas | Screenshot fidelity on your canvas-heavy targets |
| Targets behind TLS-fingerprint bot challenges | Docker Chromium | Excluded in Cloudflare’s docs for bot-challenge handshakes with real TLS fingerprints | Which of your targets gate on handshake inspection |
| Long authenticated sessions with persistent state | Docker Chromium | Kitesurf is stateless by design | Session duration against Workers limits; the announcement and Browser Run docs do not state which apply |
| Mixed pipeline | Both, routed per task | browser=kitesurf shows the routing pattern; CDP makes clients portable | Your own dispatch layer for a self-hosted split |
The pilot plan is short. Deploy the engine locally with pnpm run dev, point your existing Puppeteer or Playwright harness at it, and run your highest-volume target sites through both substrates. Record three things per site: whether the output is usable, the wall time, and which CDP calls, if any, fail. Sites that pass move to a Workers Paid deployment; sites that fail stay on the fleet, and their failure modes become the issue reports the young repository needs.
One caveat deserves to outlive this week’s release coverage: every performance, coverage, and compatibility figure in this article is Cloudflare-reported, the repository opened on 2026-11-13 so any public issue history is days old at most, and neither the announcement nor the Browser Run documentation states which Workers limits will cap long sessions on a self-hosted deployment. The workload-split framework above does not depend on any of those numbers being right. It depends only on the architecture, and the architecture is the part you can read for yourself.
Frequently Asked Questions
What are the performance differences between Kitesurf and Chromium?
Kitesurf runs 1.8× slower than Chromium on screenshot wall time (1,148 ms versus 637 ms), per Cloudflare’s Browser Run documentation, and 1.7× slower on HTML extraction (820 ms versus 472 ms). The same table reports Kitesurf using 3.1× to 3.8× less CPU and 4.7× to 7.0× less memory than Chromium, so the launch’s efficiency promise now has measurement behind it, not just a goal statement.
What workloads should stay in Docker Chromium instead of Kitesurf?
The documentation is unusually direct about scope: “Kitesurf is not yet the right option if you need to: Play video or render WebGL. Negotiate a bot-challenge handshake with real TLS fingerprints. Start a long-running, authenticated session that requires persistent state.”
How do I run Kitesurf locally for testing?
The local development loop is ordinary Workers tooling: clone the repository, pnpm install --frozen-lockfile, pnpm run dev, and wrangler serves a local engine at http://127.0.0.1:8787, which you can open in Chrome DevTools or drive programmatically with puppeteer-core over a websocket endpoint. When you are satisfied, pnpm run deploy pushes the engine to your own Cloudflare account, where it runs instead of Cloudflare’s managed Browser Run APIs.

Join the discussion
Share a useful perspective or ask a question about this article.