我们是否应该构建自己的浏览器?多年来,这个问题每隔几个月就会在 Cloudflare 内部被提起。毫不意外,这类问题往往会引发长篇讨论,列出诸多理由和具有说服力的论据,说明我们为什么应该这样做。浏览器显然是我们每天在电脑上使用的最重要的软件;它可以说是互联网的操作系统。我们是一家致力于构建更美好互联网的公司——谁不想迎接构建新浏览器的挑战呢?
但我们始终未能在这类尝试的技术难度与我们通过此举要解决的独特问题之间找到平衡。于是,这个想法一次又一次地被搁置。直到现在。
神奇的事情发生了:我们达到了一个临界点——开发者平台上的一系列强大技术进步成为现实,与此同时,AI 智能体的兴起和对新型浏览器的需求也变得至关重要。
在 Workers 中运行 WebAssembly(Wasm)如今已非常成熟。动态 Workers、基于 SQLite 的 Durable Objects、Worker 间 RPC、服务绑定、更高的 NodeJS 兼容性以及更高的限制等原语,为更宏大、更复杂的应用打开了大门,而这些在以前根本不可能实现。
Browser Run,我们的无头浏览器自动化 API 产品,随着 AI 的兴起经历了巨大的增长。智能体需要浏览器来执行许多任务,在许多情况下,没有浏览器它们就无法成功。
但有一个问题——像 Chromium 这样的浏览器引擎是为人类而非智能体构建的,它们带有 AI 模型根本不需要的开销。它们消耗大量内存和计算资源,以至于为每个智能体提供独立实例的成本高得令人望而却步,这限制了 Web 的大部分内容只能由参数知识更丰富、最复杂且最昂贵的 AI 模型访问,同时将许多其他智能体应用拒之门外。
我们应该为所有智能体提供一个在 AI 模型重要方面表现出色的浏览器,即使这意味着在仅对人类有用的方面保持轻量。例如:
- AI 并不在意标签页、主题、浏览器扩展或跨设备同步。它在意的只有 token 数量、上下文窗口、可扩展性、性能和成本。
- 结构化、机器可读的内容很重要,但视觉上的完美、流畅的 60 帧滚动并不重要。即使 CSS 解析稍有偏差,或者渲染并非像素级完美,智能体也不会介意。
- 在 AI 使用浏览器的场景下,威胁模型是不同的。提示词注入和工具安全等新问题成为首要关注点。
面对这些认知,12 周前我们再次提出了那个问题:我们是否应该构建自己的浏览器?这一次,答案是 unanimous 的:是的!
今天,我们宣布推出 Kitesurf,这是一款完全运行在 Workers 之上的新浏览器,专为智能体打造,目前已在 Browser Run 中免费提供测试版。
在常见的智能体任务(如截图和 HTML 提取)中,Kitesurf 在 CPU 和内存消耗方面比 Chromium 高效得多。以下是我们构建它的故事。系好安全带,接下来会有点技术性——但我们保证会讲得有趣。
一切是如何开始的
Kitesurf 的诞生,和 Cloudflare 许多伟大创意的起源一样。有人发现了某个有趣的东西,转眼间,他们就用一个看似不可能却又极具吸引力的想法“技术宅狙击”了整个团队。
我们的最初灵感来自 obscura,这是一个用 Rust 编写的、用于 AI 自动化的无头引擎,它“没有 Chrome,没有 Node.js,没有任何依赖”。

然后,在 AI 智能体的帮助下,我们尝试将其移植到 Workers 上。起初效果并不理想。但当我们为 AI 提供了一个扎实的计划和明确的成功定义——详细到足以让智能体无限循环并在需要时提问——它确实成功了。被这个(勉强)能用的概念验证所震撼,我们决定让团队放手一搏。
设计决策
以下是我们开始之前做出的一些设计决策。
测试,测试,再测试
我们深知,从原型走向一款功能完备、能够在生产环境中大规模处理实际任务的浏览器,需要大量的工作和反复迭代。我们不讳言,借助 AI 加速这一过程是关键。但在一项如此复杂的项目中,如何运用 AI,在保持代码与结果质量可控的同时,又不牺牲开发速度?答案是:尽可能多地提供测试。
于是我们引入了 Web Platform Tests(WPT),这是理想之选:一套覆盖面极广的达标标准,为 AI 智能体评估功能符合性提供了清晰的目标。我们精心挑选并排序了分配给智能体的功能,让人类得以专注于架构工作,并审查智能体的实现方案。
然而,WPT 测试也有其局限:它们衡量的是对 W3C 标准的符合度,而非浏览器渲染真实网站、与之交互的能力。为弥补这一差距,我们采用了集成测试与视觉回归测试相结合的方式——在真实网站上,针对 Chromium 和 Kitesurf 同时运行多步骤 Puppeteer 测试,不仅比较其断言结果,还在每一步渲染输出,以凸显任何不期望出现的差异。
尽可能使用 Rust
Cloudflare 长期以来一直在为 Workers 提供出色的 WebAssembly(Wasm)支持。这非常棒,因为我们可以使用高性能的 C、C++ 和 Rust 包,并将其编译为 Wasm。如果我们使用 Emscripten(例如)及其多层模拟依赖,编译出的二进制文件可能会变得臃肿且运行缓慢。
相反,我们尽可能选择原生 Rust,并使用 wasm-bindgen 直接编译为 WebAssembly,从而避免不必要的模拟层,尽可能贴近硬件层运行,确保可靠性。
异常处理
浏览器必须渲染整个不可靠、甚至充满恶意的网络,却绝不能丢失它当前承载的页面,因此异常处理不仅仅是代码卫生问题——它是应用在遭遇不良输入时,不至于直接崩溃、得以存续的关键机制。
因此我们一开始就定下一条铁律:任何故障都只能降级为空白帧或缺失元素,绝不能导致会话卡死。在每个边界捕获故障,默认回退到安全且为空的状态,并记录足够的日志以便诊断。
隔离
与在笔记本电脑上运行浏览器(你访问的是信任的网站,站点之间共享一些资源是可以接受的)不同,智能体面对的是任务所要求的任何内容:来自任意来源的任意代码。
因此,我们在构建这个浏览器时,假设每一次页面加载都是不可信的输入,每一个会话都从全新状态开始。每个组件都是隔离的,并且只能访问其功能所严格必需的资源。

这看起来非常适合 Cloudflare Workers,其安全模型正是围绕“设计即隔离”构建的。但该平台只为我们提供了隔离单元之间的边界。我们仍然需要在应用层面贯彻同样的原则,决定每个组件可以接触什么,并确保任何内容都不会泄漏到它不该进入的页面。
尽可能无状态
状态是让故障代价高昂的根源——如果无需重建任何东西,从崩溃中恢复就只是启动一个新实例并重放请求。无状态组件天然就是可丢弃且可并行的:一旦停滞就将其终止,可以同时运行上千个,并按需调整规模,而不是一直保持热备。这完美契合自动化场景,因为负载是突发式到达的,而最廉价的做法就是启动那些只消耗实际使用资源、用完即消失的任务。简而言之,只要组件可以无状态,就应该无状态。
我们是如何构建的
有了周密的计划、全面的测试和良好的工具环境,我们准备在初始概念验证的基础上正式开工。以下是 Kitesurf 中一个请求在极高层面上的生命周期,至今仍然适用:

让我们深入探讨让 Kitesurf 运转起来的三个主要组件:Engine、PageScript 和 PageRenderer。
从源站获取资源
为了渲染一个不可信的网页,浏览器必须从互联网上获取任意资源——图片、字体、CSS、JavaScript 和 Wasm 文件。这是浏览器能做的最危险的操作之一。
Kitesurf 通过一个单一组件——SandboxOutbound worker 来实现这一点,其他任何组件都无法直接接触网络——这一点由 Dynamic Workers 强制执行。Engine 使用它来引导页面加载,获取主文档及其脚本,而 PageScript 则获取其他所有内容:样式表、图片、字体以及页面自身的 fetch() 调用。
我们使用 SandboxOutbound 来强制执行 CORS、注入浏览器形态的请求头、过滤响应,并将每个页面的 cookie 保存在各自的独立存储中。任何不符合我们策略的请求都会收到 403 响应——每个组件都恰好获得其所需的网络访问权限,不多也不少。

Engine
Engine 是 Kitesurf 唯一面向公众的组件。它处理 Chrome DevTools 协议(CDP)WebSocket 和 HTTP REST API,提供一个用于内部测试目的且有用的落地页,并且最重要的是,它存储每个会话的状态。所有其他组件都是无状态的。

使用 CDP 的优势在于客户端兼容性:Puppeteer、Playwright、chrome-remote-interface 以及实际的 Chrome DevTools 前端。将它们指向 Kitesurf,它们都能直接正常工作。这也是 Browser Run 的工作方式(关于这一点为何重要,稍后会详细说明)。
与名称所暗示的相反,Engine 实际上是 Kitesurf 组件中最简单的一个。有趣的部分还在后面。
PageScript
PageScript 很好地展示了我们新 Workers 功能(即 Dynamic Workers)的强大之处。在此之前,Kitesurf 根本不可能实现。
这是一个简化的示意图,展示了 PageScript 的内部工作原理。

每一个新页面或进程外 iframe(OOPIF)都使用 Dynamic Workers 来启动一个长期运行的 PageScript 隔离环境,该环境负责处理页面会话,其中包含一个干净的 globalThis 和 DOM document 对象。
然后,DOM 对象会填充解析 HTML 文档和运行所有 JavaScript 脚本的结果。对于 HTML 和 CSS 的解析,我们使用 Blitz(一个模块化渲染引擎)和 Stylo(Firefox 的高性能 CSS 解析器)的部分代码,两者均用 Rust 编写。
对于每个找到的 `<script>` 标签或 `.wasm` 文件,我们在同一个隔离环境中运行 JavaScript 和 WebAssembly 代码。
是的,但 eval
你可能会问,那 eval 呢?eval 处理起来更棘手,因为出于安全原因,我们目前还不支持在 Workers 中原生使用 eval。我们也不能再启动一个隔离区来处理它们,因为那样就无法访问 globalThis。
我们的解决方案是使用 Boa JS——一个用 Rust 编写的 ECMAScript 引擎——在 Workers 上进行编译和运行。我们基本上是在一个运行时之上再执行一个运行时,这看起来并非最优方案,实际上也确实不是,但它足以处理我们在代码中偶尔遇到的 eval。未来,当 Workers 原生支持 eval 时,我们将从 Boa 迁移出去。
PageRenderer
这个组件主要负责根据计算出的页面对象生成实际的像素。其工作原理如下:

PageRenderer 与 Engine Worker 在一个循环中协同工作。每当引擎需要一帧时,PageRenderer 会从 PageScript(也称为场景)获取页面对象,从 Static Assets 获取内部字体和图像,将所有内容栅格化到图像缓冲区中,然后将缓冲区以客户端可显示的格式(如 JPEG/PNG 或 PDF)返回给引擎。
这里的很大一部分魔法由另一个 Blitz 模块 blitz-paint 处理,该模块又使用 Parley 将字符塑形为字形、选择字体,并将文本断成行。
Workers 内置的 RPC 系统:同一应用,多个隔离区
Cloudflare Workers 有一个内置的远程过程调用(RPC)系统,允许你调用其他 Worker 上的方法、在它们之间传递对象,并调用这些对象上的方法。你无需担心 API 模式、类型或身份验证,只需调用 remoteFunction(...params) 即可。你既能受益于远程 Worker 的隔离性和资源,又不会失去使用 JavaScript 在本地访问其所有函数的便利性。
Kitesurf 使用这套 RPC 系统:Engine Worker 通过 RPC 以单次调用方式调用 PageRenderer Worker 的 renderFrame(),并得到一张 PNG 作为结果。由于渲染器不持有页面状态(仅有一个可丢弃的缓存),引擎可以在任何失败或卡住的 RPC 调用时安全地将其杀死并重新启动——这使得每个渲染请求都是自包含、可重试的,其 isolate 也廉价且可随时丢弃。
Kitesurf 已通过 215,000+ 项 WPT 测试,且数量还在增长
Kitesurf 是可行的。它已经通过了大约 215,000+ 项 WPT 测试,而且我们每周都会新增数百项通过的测试。这里你可以看到自项目启动以来,直到最新版本的通过情况随时间的变化:

值得注意的是,浏览器中对智能体重要的部分(例如 CSS、DOM、HTML、selection、SVG 和 XHR)已经有了很好的覆盖率。即使是那些在智能体场景下可能不太重要的部分,比如 streams,现在也得到了不错的支持。

在性能方面,Kitesurf 表现相当不错。下面是在一个包含 14 个 URL 的语料库上,对 Chromium 和 Kitesurf 进行五次 Browser Run 快速操作运行的中位数对比。
指标 | Kitesurf | Chromium(热池) | Kitesurf 相对表现 |
|---|---|---|---|
CPU:截图 | 380 毫秒 | 1,173 毫秒 | 比 Chromium 节省 3.1 倍 CPU |
CPU:HTML 提取 | 229 毫秒 | 877 毫秒 | 比 Chromium 节省 3.8 倍 |
内存:截图 | 57.8 MiB | 271.0 MiB | 比 Chromium 节省 4.7 倍 |
内存:HTML 提取 | 39.4 MiB | 273.7 MiB | 比 Chromium 节省 7.0 倍 |
墙钟时间:截图 | 1,148 毫秒 | 637 毫秒 | 比 Chromium 慢 1.8 倍 |
墙钟时间:HTML 提取 | 820 毫秒 | 472 毫秒 | 比 Chromium 慢 1.7 倍 |
Chromium 在计时上胜出,因为一个已经见过此页面的 JIT 总是能胜过冷启动的软件渲染器——目前确实如此,大约快 1.7 倍。这个差距大部分来自光栅化和 JPEG/PNG 编码,我们将持续优化这些方面。
但 Kitesurf 在内存和 CPU 上胜出,而这两者才是真正决定你账单的因素,相比 Chromium 的消耗节省了 3-7 倍。更少的内存意味着我们可以运行更多会话、更好地扩展,并从根本上来降低我们双方的成本。
最重要的测试:Kitesurf 能运行 Doom
我们在设计决策中强调了测试的重要性,但众所周知,无论你有多少测试,一个项目直到 Doom 能在上面跑起来才算真正完成。下面展示的是 Kitesurf 运行 https://silentspacemarine.com/ 的画面,这来自我们几年前做的一个小小的 Doom 实验。
今天就通过 Browser Run 试试吧
你现在就可以通过 Browser Run 体验 Kitesurf,测试版期间免费使用,但受每账号限额限制。
Browser Run 的 CDP 端点现在支持将 Kitesurf 作为一个选项,因此你现有的客户端 Puppeteer、Playwright、chrome-remote-interface,或任何支持 MCP 和 CDP 的 AI 智能体,都可以直接使用。你只需在我们的端点上添加 `browser=kitesurf` 参数即可。
例如,要在 Opencode 中使用 Kitesurf,请参阅我们开发者文档中的“与 MCP 客户端(CDP)一起使用”部分,并使用以下配置:
{
"mcp": {
"kitesurf": {
"type": "local",
"command": [
"npx",
"-y",
"chrome-devtools-mcp@latest",
"--wsEndpoint=wss://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/browser-run/devtools/browser?browser=kitesurf",
"--wsHeaders={\"Authorization\":\"Bearer <API_TOKEN>\"}"
],
"enabled": true
}
}
} 使用 Kitesurf 的另一种方式是通过 Browser Run 的快捷操作。同样,只需在快捷操作端点添加 `browser=kitesurf` 参数即可生效。例如,如果你需要快速截取维基百科的页面截图,这样操作完全没问题:
curl -X POST 'https://api.cloudflare.com/client/v4/accounts/<accountId>/browser-run/screenshot?browser=kitesurf' \
-H 'Authorization: Bearer <apiToken>' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://example.com"
}' \
--output "screenshot.png" 使用带 Chrome DevTools 的 Kitesurf Playground
另一个开始探索 Kitesurf 的方式是使用我们这里的公共 playground。你可以输入任意 URL,查看 Kitesurf 如何渲染该页面并与之交互。
Playground 的一个有趣特性是,我们在界面中注入了 Chrome DevTools,因此你可以在 Kitesurf 渲染页面时检查展开后的 DOM 元素、读取控制台消息,并观察网络活动。更有趣的是,我们为 Memory 面板实现了必要的 CDP 指令,用于报告每个隔离区的 WebAssembly 内存占用(包括帧),这样你就能清晰了解每个页面消耗的资源情况。

有关如何将 Kitesurf 与 Browser Run 结合使用的所有详细信息,请查阅我们的开发者文档。
Kitesurf 什么时候表现更好?
截至目前,Kitesurf 能正确渲染 TodoMVC(vanilla、React、Vue、Angular、Preact)、维基百科、Hacker News、Cloudflare Blog 以及 Cloudflare 控制台的大部分页面。我们将持续改进 Kitesurf,提高 WPT 测试的通过率,以提升对更复杂网页的兼容性。
Kitesurf 非常适合需要渲染页面、但能接受不使用功能完整、像素级完美的 Chromium 浏览器的 AI 智能体。对于依赖一次性快速操作(例如从页面提取内容、生成 PDF 或截图)的自动化流程和应用,它同样表现出色,前提是目标站点兼容。
可以把 Kitesurf 理解为一个临时性、完全隔离、无状态的引擎,它只为某项任务存续期间而存在,并且能很好地扩展以应对突发性的、由 AI 驱动的工作负载。
Kitesurf 目前尚不能做到的事情
如果你需要播放视频、渲染 WebGL、使用真实 TLS 指纹完成机器人挑战握手,或者启动一个需要持久状态的十分钟认证会话——Kitesurf 目前还不是合适的选择。这种情况下,直接使用 Browser Run 的默认配置即可,它由 Chromium 驱动。
判断某个特定站点是否与 Kitesurf 兼容的最佳方式就是直接尝试。你可以通过 API 来测试,或者更快的方式是在我们的公共 playground 中试用。
探索 DevTools 面板,看看后台发生了什么,尤其要关注控制台和内存指标。
未来发展方向
Kitesurf 才诞生十二周。第一次代码提交是在五月。以下是我们正在积极推进的一些事项:
- 更完善的 CDP 覆盖。Kitesurf 实现了 CDP 协议的一个子集——足以满足大多数智能体和自动化工具的需求,包括健壮的 DOM 和网络检查——并且我们持续扩展其能力,力求尽可能完整。
- 提升截图和 PDF 的渲染保真度,因为我们知道,大语言模型通常能从图像中比从底层文本中更好地理解内容。
- WPT 覆盖。我们正在快速迭代,以增加更多 Web API 并通过更多 WPT 测试,朝着让 Kitesurf 达到生产就绪状态的目标迈进。
- 效率。我们持续运行 CPU、内存和墙钟时间基准测试,并与其他开发者平台团队紧密合作,力求让 Kitesurf 尽可能经济高效。
结语
感谢您一路读到这里——我们知道这是一篇篇幅较长且技术性强的博客文章,但希望它足够有趣。我们之所以写得如此详细,是因为我们深知构建一款新浏览器(即便是非常特定用途的浏览器)既重要又复杂,我们对此绝不敢掉以轻心。
Kitesurf 目前仍处于早期阶段,但我们希望尽快向您开放,并听取您的反馈。团队将积极改进它,并围绕性能、效率和兼容性进行高频更新。
最后一点:一旦准备就绪,我们就会将 Kitesurf 开源——希望很快就能实现。我们的目标是让任何客户都能按需在自己的账户上部署他们自己的 Kitesurf 版本。
所以,欢迎在 playground 中试用,持续关注我们的 changelog,并来 Discord 与团队交流。分享您的体验并给我们反馈,我们一直在倾听。