Kitesurf 是面向 AI 智能体设计的无状态浏览器,完全运行于 Cloudflare Workers 的 V8 隔离环境。其目标是以可水平扩展的方式提供网页操作能力并控制运行成本。
我们应该开发自己的浏览器吗?多年来,这个问题每隔几个月就会在 Cloudflare 内部被提起一次。不出所料,这类问题总会引发冗长的讨论,大家会列出各种理由,并提出极具说服力的论点,说明我们为什么应该这么做。浏览器显然是我们每天使用计算机时最重要的软件;甚至可以说,它就是互联网的操作系统。作为一家以构建更好的互联网为使命的公司,谁会不想迎接开发一款全新浏览器的挑战呢?
但我们始终未能在这项工作的技术难度与它能够解决的独特问题之间找到平衡。因此,这个想法一次又一次地被搁置。直到现在。
一些奇妙的变化发生了:我们到达了一个临界点。开发者平台的一系列强大技术进展成为现实,与此同时,AI 智能体的兴起,以及对一种新型浏览器的需求,也变得至关重要。
如今,在 Workers 中运行 WebAssembly(Wasm)已经非常成熟。动态 Workers、基于 SQLite 的 Durable Objects、Worker 间 RPC、服务绑定、更高的 NodeJS 兼容性以及更宽松的限制等基础能力,为更具雄心、更加复杂的应用打开了大门——这些应用在过去根本无法实现。
随着 AI 的兴起,我们的无头浏览器自动化 API 产品 Browser Run 取得了惊人的增长。AI 智能体需要浏览器来执行许多任务,而且在很多情况下,没有浏览器就无法成功完成任务。
但这里存在一个问题——Chromium 之类的浏览器引擎是为人类而构建的,而不是为 AI 智能体设计的,因此带有许多 AI 模型根本不需要的额外开销。它们消耗的内存和计算资源非常多,以至于为每个 AI 智能体分别提供一个实例的成本高得令人难以承受。这使得 Web 上的大量内容只能由参数化知识更丰富、最先进但也最昂贵的 AI 模型访问,同时将许多其他智能体应用拒之门外。
我们应该为所有 AI 智能体提供一种浏览器,让它在 AI 模型真正看重的方面表现出色,即便这意味着弱化那些只对人类有用的能力。例如:
AI 不关心标签页、主题、浏览器扩展,也不关心跨设备同步。它关心的是 token 数量、上下文窗口、可扩展性、性能和成本。
结构化、机器可读的内容很重要,但视觉上的完美以及流畅的 60 fps 滚动并不重要。即使 CSS 解析略有偏差,或者渲染结果无法做到像素级完美,AI 智能体也完全可以正常工作。
在 AI 使用浏览器的场景中,威胁模型也有所不同。提示词注入和工具安全等新问题成为首要任务。
面对这些认识,12 周前,我们再次提出了这个问题:我们应该开发自己的浏览器吗?这一次,答案全票通过:应该!
今天,我们宣布推出 Kitesurf。这是一款完全运行在 Workers 之上、专为 AI 智能体打造的新型浏览器。在测试期间,可以通过 Browser Run 免费使用。
对于截图和 HTML 提取等常见的智能体任务,Kitesurf 的 CPU 和内存消耗远低于 Chromium。接下来,我们将讲述它的构建过程。系好安全带,接下来的内容会相当技术化——但我们保证,它会很有趣。
和 Cloudflare 的许多其他绝妙创意一样,Kitesurf 也始于这样的场景:有人发现了一个有趣的东西,转眼之间,他就用一个看似不可能、却极具吸引力的想法成功地对团队其他成员实施了“技术诱捕”。
我们最初的灵感来自 obscura,这是一个用 Rust 编写、用于 AI 自动化的无头引擎,号称“没有 Chrome、没有 Node.js、没有依赖项”。

随后,我们在一个 AI 智能体的帮助下,尝试将其移植到 Workers。起初,效果并不理想。但当我们为 AI 提供了一份可靠的计划和清晰的成功标准——详细到足以让 AI 智能体不断循环执行,并在需要时提出问题——它真的成功了。这个勉强能够运行的概念验证让我们大为震撼,于是我们决定放手让团队尽情发挥。
以下是我们在开始之前做出的一些设计决策。
我们知道,要从一个原型发展成一款真正完整的浏览器,使其能够在生产环境中大规模执行有实际价值的任务,需要大量工作和反复迭代。我们不会掩饰这样一个事实:使用 AI 加速这一过程至关重要。但在如此复杂的项目中,应该如何使用 AI,既能控制代码和结果的质量,又不会牺牲开发速度?答案是:提供尽可能多的测试。
于是,Web Platform Tests(WPT)登场了,这是理想的测试环境:一套范围广泛的成功标准,为 AI 智能体评估功能一致性提供了清晰的目标。我们精心筛选了要分配给 AI 智能体的功能及其实现顺序,让人类能够专注于架构工作,并审查 AI 智能体采用的实现方案。
然而,WPT 测试能够覆盖的范围有限:它们衡量的是对 W3C 标准的符合程度,而不是浏览器渲染真实网站并与之交互的能力。为了弥合这一差距,我们实现了一套结合了集成测试和视觉回归测试的方案——它会使用 Puppeteer,分别针对 Chromium 和 Kitesurf,在真实网站上运行多步骤测试。它不仅会比较测试中的断言,还会渲染每一步的输出,以突出显示任何非预期差异。
尽可能使用 Rust
一段时间以来,Cloudflare 一直致力于为 Workers 中的 WebAssembly(Wasm)提供出色支持。这非常有价值,因为我们可以使用高性能的 C、C++ 和 Rust 软件包,并将它们编译为 Wasm。如果使用 Emscripten 及其多层模拟依赖项,编译后的二进制文件可能会变得臃肿且运行缓慢。
因此,我们决定尽可能使用原生 Rust,并通过 wasm-bindgen 直接编译为 WebAssembly,从而避开不必要的模拟层,以尽可能接近底层硬件的方式可靠运行。
浏览器必须渲染整个不可靠、甚至有时充满恶意的 Web,同时绝不能丢失当前承载的页面。因此,异常处理不仅仅是保持代码整洁的问题——它决定了应用能否在面对错误输入时继续存活,而不是直接崩溃。
所以,我们从一开始就确立了一条规则:任何故障都只能降级为空白帧或缺失元素,绝不能导致会话终止。在每一处边界捕获错误,默认返回安全的空内容,并记录足够的信息以便诊断。
这不同于在笔记本电脑上运行浏览器的场景——在电脑上,你访问的是自己信任的网站,并且允许它们共享某些资源;而 AI 智能体会根据任务要求访问任何内容,也就是来自任意来源的任意代码。
因此,我们构建这款浏览器时始终基于这样的假设:每次页面加载都是不可信输入,每个会话都要从全新状态开始。每个组件都彼此隔离,并且只能访问其功能所必需的资源。

这似乎与 Cloudflare Workers 完美契合,因为它的安全模型从设计之初就建立在隔离之上。但平台只能为我们提供 isolate 之间的边界。我们仍然必须在应用层实施同样的原则,决定每个组件可以访问什么,并确保任何内容都不会泄漏到不该接触它的页面中。
尽可能保持无状态
状态会让故障恢复变得昂贵——如果没有任何内容需要重建,那么从崩溃中恢复就只是启动一个新实例并重新执行请求。无状态组件天然具有可丢弃和可并行的特性:一旦它停滞就将其终止,同时运行一千个实例,并根据需求调整规模,而不必维持预热状态。这与自动化场景完美契合,因为负载会突发式到来,而成本最低的做法就是启动工作任务,只为它实际使用的资源付费,并在任务完成后立即消失。简而言之,只要一个组件可以做到无状态,它就应该保持无状态。
有了完善的计划、广泛的测试和良好的工具环境,我们已经准备好跨越最初的概念验证阶段,正式开始构建。下面是 Kitesurf 从请求进入到完成处理的高度概括流程,直到今天仍然如此:

接下来,让我们深入了解支撑 Kitesurf 运行的三个主要组件:Engine、PageScript 和 PageRenderer。
从源站获取资源
为了渲染不可信的网页,浏览器必须从互联网上获取任意资源——图片、字体、CSS、JavaScript 和 Wasm 文件。这是浏览器所执行的最危险操作之一。
Kitesurf 只通过一个组件执行这项操作,即 SandboxOutbound Worker,其他任何组件都不能直接访问网络——这一限制由 Dynamic Workers 强制实施。Engine 使用它来引导页面启动,获取主文档及其脚本;PageScript 则负责获取其他所有内容:样式表、图片、字体,以及页面自身发出的 fetch() 请求。
我们使用 SandboxOutbound 强制执行 CORS、注入浏览器风格的请求头、过滤响应,并将每个页面的 Cookie 分别保存在各自的 Cookie Jar 中。任何不符合策略的请求都会收到 403 响应——每个组件都只能获得其所需的网络访问权限,不多也不少。

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

使用 CDP 的优势在于客户端兼容性:Puppeteer、Playwright、chrome-remote-interface,以及真正的 Chrome DevTools 前端。只需将它们指向 Kitesurf,就都能直接工作。Browser Run 也是以这种方式工作的(稍后会进一步说明这为什么很重要)。
与名称给人的印象相反,Engine 实际上是 Kitesurf 所有组件中最简单的一个。精彩的部分还在后面。
PageScript 很好地展示了 Workers 新功能的强大之处:在这里具体指 Dynamic Workers。没有它,Kitesurf 根本不可能实现。
下面是一张简化图,展示了 PageScript 的内部工作原理。

每个新页面或进程外 iframe(OOPIF)都会使用 Dynamic Workers 启动一个长生命周期的 PageScript isolate,由它负责处理页面会话,其中包含一个干净的 globalThis 和 DOM document 对象。
随后,DOM 对象会填充解析 HTML 文档和运行所有 JavaScript 脚本所得的结果。对于 HTML 和 CSS 的解析,我们使用了模块化渲染引擎 Blitz 的部分组件,以及 Firefox 的高性能 CSS 解析器 Stylo,二者均使用 Rust 编写。
对于找到的每个 <script> 标签或 .wasm 文件,我们都会在同一个 isolate 内运行相应的 JavaScript 和 WebAssembly 代码。
你可能会问,eval 怎么办?eval 的处理更加棘手,因为出于安全原因,我们目前仍未在 Workers 中原生支持 eval。我们也不能启动另一个 isolate 来处理它,因为新的 isolate 无法访问 globalThis。
我们的解决方案是使用以 Rust 编写的 ECMAScript 引擎 Boa JS,在 Workers 上编译并运行代码。基本上,我们是在一个运行时之上执行另一个运行时,这看起来并非最优方案——事实也确实如此——但它足以处理代码中偶尔出现的 eval。未来,当 Workers 支持原生 eval 后,我们会迁移并停止使用 Boa。
这个组件本质上负责根据计算后的页面对象生成实际像素。它的工作方式如下:

PageRenderer 与 Engine Worker 以循环方式协同工作。每当 Engine 需要一帧画面时,PageRenderer 就会从 PageScript 获取页面对象(也称为场景),从 Static Assets 获取内部字体和图像,将所有内容光栅化到图像缓冲区中,然后以客户端可以显示的格式(例如 JPEG、PNG 或 PDF)将缓冲区返回给 Engine。
这里很大一部分魔法由 Blitz 的另一个模块 blitz-paint 完成,而它又使用 Parley 将字符塑形为字形、选择字体,并将文本分割成多行。
Cloudflare Workers 内置了远程过程调用(RPC)系统,允许你调用其他 Workers 上的方法、在它们之间传递对象,以及调用这些对象上的方法。你不必操心 API schema、类型或身份验证,只需调用 remoteFunction(...params),它就能正常工作。这样既能受益于远程 Worker 的隔离性和资源,又不会失去通过 JavaScript 在本地访问其全部函数的便利性。
Kitesurf 使用了这套 RPC 系统:Engine Worker 只需通过一次 RPC 调用 PageRenderer Worker 的 renderFrame(),便能获得一个 PNG 作为结果。由于渲染器不保存任何页面状态(只有一个可随时丢弃的缓存),Engine 可以在任何 RPC 调用失败或卡住时安全地终止并重新启动它——这让每次渲染请求都相互独立、可以重试,并且其 isolate 成本低廉、可随时丢弃。
Kitesurf 确实可以工作。它目前已经通过大约 215,000 多项 WPT 测试,而且我们每周都会新增数百项通过的测试。下图展示了从项目启动至最新版本,其测试通过数量随时间的变化:

值得注意的是,浏览器中对智能体非常重要的部分(例如 CSS、DOM、HTML、文本选择、SVG 和 XHR)已经拥有良好的测试覆盖率。甚至流(streams)这种在智能体场景中可能没那么重要的功能,现在也得到了不错的支持。

在性能方面,Kitesurf 的表现相当不错。下面是在包含 14 个 URL 的语料集上分别运行五次 Browser Run Quick Actions 后的中位数,并将 Chromium 与 Kitesurf 进行了比较。
CPU 使用量比 Chromium 低 3.1 倍
比 Chromium 低 3.8 倍
比 Chromium 低 4.7 倍
内存:HTML 提取
比 Chromium 低 7.0 倍
实际耗时:截图
比 Chromium 慢 1.8 倍
实际耗时:HTML 提取
比 Chromium 慢 1.7 倍
Chromium 在计时对比中获胜,因为已经处理过这个页面的 JIT 总是能击败冷启动的软件渲染器——目前确实如此,速度大约快 1.7 倍。这一差距主要来自光栅化以及 JPEG/PNG 编码,我们会继续对其进行优化。
但 Kitesurf 在内存和 CPU 方面胜出,而这两项才是真正决定账单金额的因素:与 Chromium 相比,Kitesurf 的资源用量低 3~7 倍。更少的内存占用意味着我们可以运行更多会话、实现更好的扩展能力,并从根本上降低我们和你的成本。
我们在设计决策中强调了测试的重要性,但大家都知道,无论有多少测试,一个项目只有能够运行 Doom 才算真正完成。下面展示的是 Kitesurf 运行 https://silentspacemarine.com/ 的效果,这是我们几年前做的一个小型 Doom 实验。
你现在就可以通过 Browser Run 试用 Kitesurf。Beta 期间可以免费使用,但受到每个账户的额度限制。
Browser Run 的 CDP 端点现在已支持选择 Kitesurf,因此你现有的客户端——Puppeteer、Playwright、chrome-remote-interface,或任何支持 MCP 和 CDP 的 AI 智能体——都已经可以直接使用。你只需在我们的端点中添加 browser=kitesurf 参数即可。
例如,要在 Opencode 中使用 Kitesurf,请参阅开发者文档中的 Using with MCP clients (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 的 Quick Actions。同样,只需在 Quick Action 端点中添加 browser=kitesurf 即可。例如,如果你需要快速截取 Wikipedia 的页面截图,下面的命令就能正常工作:
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"
另一种开始探索 Kitesurf 的方式是使用我们的公共 Playground。你可以输入任意 URL,查看 Kitesurf 如何渲染页面并与其交互。
Playground 的一个有趣功能是,我们在 UI 中嵌入了 Chrome DevTools,因此当 Kitesurf 渲染页面时,你可以检查展开后的 DOM 元素、读取控制台消息并观察网络活动。更有意思的是,我们实现了必要的 CDP 指令,使 Memory 面板能够报告每个 isolate 的 WebAssembly 内存占用情况,包括各个 frame,从而帮助你清楚了解每个页面正在消耗的资源。

有关如何通过 Browser Run 使用 Kitesurf 的全部细节,请查看我们的开发者文档。
截至目前,Kitesurf 已经能够正确渲染 TodoMVC(vanilla、React、Vue、Angular、Preact)、Wikipedia、Hacker News、Cloudflare Blog,以及 Cloudflare 控制面板的大部分页面。我们将继续改进 Kitesurf,提高 WPT 测试的通过比例,从而提升对更复杂网页的兼容性。
Kitesurf 非常适合需要渲染页面,但可以接受不使用功能完备、像素级精确的 Chromium 浏览器这一取舍的 AI 智能体。对于依赖一次性快速操作(Quick Actions)的自动化任务和应用,例如从页面中提取内容,或者为兼容的网站生成 PDF 或截图,它同样非常出色。
你可以将 Kitesurf 看作一个临时存在、完全隔离且无状态的引擎:它仅在任务执行期间存在,并且能够很好地应对突发式、由 AI 驱动的工作负载。
如果你需要播放视频、渲染 WebGL、使用真实 TLS 指纹完成反机器人挑战握手,或者启动一个需要持久状态、持续十分钟的身份验证会话,那么 Kitesurf 目前还不是合适的选择。直接使用 Browser Run 由 Chromium 驱动的默认方案即可。
要确认某个特定网站是否兼容 Kitesurf,最好的办法就是亲自尝试。你可以通过 API 进行测试,或者更快捷地在我们的公共 playground 中试用。
浏览 DevTools 的各个面板,了解幕后发生的情况,并特别关注控制台和内存指标。
Kitesurf 诞生至今只有十二周。第一次代码提交是在五月。以下是我们正在积极推进的一些工作:
改进 CDP 覆盖范围。 Kitesurf 实现了 CDP 协议的一个子集,足以满足大多数智能体和自动化工具的需求,包括强大的 DOM 与网络检查能力。我们仍在不断扩展其功能,力求使其尽可能完整。
提高截图和 PDF 的渲染保真度。 因为我们知道,与底层文本相比,LLM 通常能够更好地处理图像。
扩大 WPT 覆盖范围。 在推动 Kitesurf 达到生产就绪状态的过程中,我们正在快速迭代,加入更多 Web API,并通过更多 WPT 测试。
提升效率。 我们持续运行 CPU、内存和实际耗时基准测试,并与其他开发者平台团队密切合作,力求使 Kitesurf 尽可能经济高效。
感谢你坚持读到这里——我们知道这是一篇篇幅很长、技术性很强的博客文章,但希望它也足够有趣。我们之所以深入介绍这些细节,是因为我们非常清楚,构建一款新浏览器——即使是一款用途非常具体的浏览器——既极其重要,也极其复杂,绝不能掉以轻心。
Kitesurf 仍处于早期阶段,但我们希望尽快向你开放,并从你的反馈中学习。团队将持续积极改进它,频繁发布更新,重点提升性能、效率和兼容性。
最后还有一件事:准备就绪后,我们将开源 Kitesurf——希望这一天很快到来。我们的目标是让任何客户都能根据需要,在自己的账户中部署自己的 Kitesurf 版本。
所以,欢迎前往 playground 亲自试用,关注我们的更新日志,并在 Discord 上与团队交流。请分享你的使用体验并向我们提供反馈;我们会认真倾听。