我们该自己造一个浏览器吗?
多年来,Cloudflare 内部每隔几个月就会冒出这个问题。毫不意外,它总会引发长长的讨论串,里面有各种理由,也有关于为什么该做的有力论证。浏览器显然是我们每天在电脑上用得最多的软件,可以说是互联网的操作系统。我们这家公司的使命就是帮助建设更好的互联网,谁不想接下造一个新浏览器的挑战呢?
但我们始终没能在这件事的技术难度与做成它所能解决的独特问题之间找到平衡。于是这个想法一次又一次被搁置。直到现在。
神奇的事情发生了:我们的开发者平台上一系列强大的技术进步成为现实,与此同时,AI 智能体的出现以及对新型浏览器的需求也变得迫切起来,两者恰好同时到达了临界点。
在 Workers 中运行 WebAssembly(Wasm)如今已非常成熟。动态 Worker、基于 SQLite 的 Durable Objects、Worker 到 Worker 的 RPC、服务绑定、更高的 NodeJS 兼容性和更高的限额等基础能力,为过去根本不可能实现的、更有野心也更复杂的应用打开了大门。
我们的无头浏览器自动化 API 产品 Browser Run,随着 AI 的兴起实现了巨大增长。智能体需要浏览器来完成许多任务,而且在很多情况下,没有浏览器就无法成功。
但有一个问题:Chromium 这类浏览器引擎是为人类而非智能体打造的,带有 AI 模型根本不需要的开销。它们消耗大量内存和算力,为每个智能体都提供一个独立实例,成本高得难以承受,结果就是 Web 的很大一部分只向最复杂、最昂贵、参数化知识更丰富的 AI 模型开放,把许多其他智能体应用挡在门外。
我们应该给所有智能体提供一个浏览器,让它擅长 AI 模型真正看重的东西,哪怕这意味着在只对人类有用的方面做减法。例如:
- AI 不在乎标签页、主题、浏览器扩展或跨设备同步。它在乎的是 token 数量、上下文窗口、可扩展性、性能和成本。
- 结构化、机器可读的内容很重要,但视觉上的完美、流畅的 60 fps 滚动并不重要。即使 CSS 解析略有偏差,或者渲染不是像素级精确,智能体也完全没问题。
- AI 使用浏览器时的威胁模型也不同。提示词注入和工具安全这类新问题是首要任务。
有了这些认识,12 周前我们再次提出这个问题:我们该自己造一个浏览器吗?这一次,答案是一致的:该!
今天,我们宣布推出 Kitesurf,这是一款完全运行在 Workers 之上、专为智能体打造的新浏览器,在 Browser Run 中测试期间免费提供。
在截图和 HTML 提取这类常见的智能体任务上,Kitesurf 在 CPU 和内存消耗方面明显比 Chromium 更高效。下面是我们如何构建它的故事。系好安全带,接下来会很技术化,但我们保证让它保持有趣。
缘起
Kitesurf 的开端和 Cloudflare 的许多好点子一样。有人发现了一个有趣的东西,接着你就会发现,他用一个看似不可能却极具诱惑的想法,把团队其他人也“书呆子式地吸引(nerd sniping)”进来了。
最初的灵感来自 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,从而避开不必要的模拟层,尽可能贴近底层硬件运行,而且可靠。
异常处理
浏览器必须渲染整个不可靠、有时甚至充满敌意的 Web,而且绝不能丢掉手上的页面,所以异常处理不只是代码卫生,而是应用在面对糟糕输入时不至于直接崩溃的生存之道。
因此,我们一开始就确立了一条规则:任何故障都只会降级为空白帧或缺失的元素,绝不会让会话死掉。在每个边界处捕获故障,默认回退到安全的空值,并记录足够的信息以便诊断。
隔离
在笔记本电脑上运行浏览器时,你访问的是自己信任的网站,在它们之间共享一些资源也可以接受;而智能体则会被指向任务所需的任何地方:来自任意源的任意代码。
因此,我们构建这个浏览器时假定每次页面加载都是不受信任的输入,每个会话都从全新状态开始。每个组件相互隔离,只能访问其功能严格必需的资源。

这似乎与 Cloudflare Workers 完美契合,其安全模型在设计上就以隔离为核心。但平台只能给我们 isolate 之间的边界。我们仍需在应用层面贯彻同样的原则:决定每个组件被允许接触什么,并确保任何内容都不会泄漏到它不该进入的页面。
尽可能无状态
状态让故障代价高昂:如果没有什么需要重建,从崩溃中恢复就只是启动一个新的并重放请求。无状态组件天生可抛弃、可并行:一旦卡住就立刻终止,可以同时运行一千个,并按需求调整规模,而不是让一切保持预热。这非常适合自动化场景,因为负载是突发到来的,最划算的做法就是启动只按实际用量计费、完成后即消失的任务。简而言之,只要组件能做到无状态,就应该无状态。
我们如何构建它
有了好的计划、详尽的测试和良好的工具环境,我们准备好在最初的概念验证之外开始行动。下面是 Kitesurf 一个请求的生命周期的高层视图,至今依然成立:

让我们深入了解让 Kitesurf 运转的三个主要组件:Engine、PageScript 和 PageRenderer。
从源站获取资源
要渲染一个不受信任的网页,浏览器必须从互联网上获取任意资源:图片、字体、CSS、JavaScript 和 Wasm 文件。这是浏览器所能做的最危险的操作之一。
Kitesurf 通过唯一的一个组件,也就是 SandboxOutbound Worker 来完成这件事,其他任何东西都不能直接接触网络,这一点由 Dynamic Workers 强制保证。Engine 用它来引导页面,获取主文档及其脚本,而 PageScript 获取其他所有内容:样式表、图片、字体,以及页面自身的 fetch() 调用。
我们用 SandboxOutbound 来强制执行 CORS、注入浏览器风格的请求头、过滤响应,并让每个页面的 cookie 各自存放在独立的 jar 中。任何不符合策略的请求都会得到 403——每个组件只获得它所需的网络权限,不多一分。

Engine
Engine 是 Kitesurf 中唯一面向公网的组件。它负责处理 Chrome DevTools Protocol(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 isolate 来处理页面会话,其中包含一个干净的 globalThis 和 DOM document 对象。
随后,DOM 对象会被填充 HTML 文档的解析结果以及所有 JavaScript 脚本的运行结果。解析 HTML 和 CSS 时,我们用到了模块化渲染引擎 Blitz 和 Firefox 的高性能 CSS 解析器 Stylo 的部分代码,两者都是用 Rust 编写的。
对于找到的每个 <script> 标签或 .wasm 文件,我们都在同一个 isolate 中运行其中的 JavaScript 和 WebAssembly 代码。
没错,但 eval 怎么办
你可能会问 eval 呢?eval 比较棘手,因为出于安全原因,Workers 目前仍不原生支持 eval。我们也无法再启动另一个 isolate 来处理它们,因为那个 isolate 访问不到 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 系统:同一个应用,多个 isolate
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 quick-action 的中位数对比。
| 指标 | Kitesurf | Chromium(预热池) | Kitesurf 相对值 |
|---|---|---|---|
| CPU:截图 | 380 ms | 1,173 ms | CPU 占用比 Chromium 少 3.1 倍 |
| CPU:HTML 提取 | 229 ms | 877 ms | 比 Chromium 少 3.8 倍 |
| 内存:截图 | 57.8 MiB | 271.0 MiB | 比 Chromium 少 4.7 倍 |
| 内存:HTML 提取 | 39.4 MiB | 273.7 MiB | 比 Chromium 少 7.0 倍 |
| 耗时:截图 | 1,148 ms | 637 ms | 比 Chromium 慢 1.8 倍 |
| 耗时:HTML 提取 | 820 ms | 472 ms | 比 Chromium 慢 1.7 倍 |
在秒表上是 Chromium 赢了,因为已经见过这个页面的 JIT 总是能胜过冷启动的软件渲染器——目前差距约为 1.7 倍。这个差距大部分来自光栅化和 JPEG/PNG 编码,我们会继续优化。
但在真正决定账单的内存和 CPU 上,Kitesurf 比 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 的 Quick Actions。同样,只需把 browser=kitesurf 加到 quick action 端点上就行。例如,如果你需要一张维基百科的快速截图,这样就可以:
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 指令,让它能报告每个 isolate(包括各个 frame)的 WebAssembly 内存占用,这样你就能清楚了解每个页面消耗了多少资源。

关于如何在 Browser Run 中使用 Kitesurf 的全部细节,请查阅我们的开发者文档。
Kitesurf 何时更合适?
截至目前,Kitesurf 可以正确渲染 TodoMVC(原生、React、Vue、Angular、Preact 版本)、维基百科、Hacker News、Cloudflare 博客以及 Cloudflare 控制台的大部分页面。我们会持续改进 Kitesurf,提高通过的 WPT 测试比例,以兼容更复杂的网页。
对于需要渲染页面、但可以接受不使用功能完整、像素级精确的 Chromium 浏览器这一取舍的 AI 智能体,Kitesurf 非常合适。对于依赖一次性 Quick Actions 的自动化和应用,例如从兼容站点提取页面内容或生成 PDF、截图,它同样表现出色。
可以把 Kitesurf 看作一个临时的、完全隔离的无状态引擎,只为任务期间而存在,非常适合突发的 AI 驱动型工作负载。
Kitesurf 目前还做不到什么
如果你需要播放视频、渲染 WebGL、用真实的 TLS 指纹完成反机器人挑战握手,或者开启一个需要持久状态、长达十分钟的已认证会话——Kitesurf 目前还不是合适的选择。请直接使用 Browser Run 默认的、由 Chromium 驱动的方案。
判断某个站点是否与 Kitesurf 兼容,最好的办法就是试一试。你可以通过 API 来试,或者更快地在我们的公共 playground 中试用。
不妨探索各个 DevTools 面板,看看幕后发生了什么,尤其留意控制台和内存指标。
未来方向
Kitesurf 才诞生十二周,第一次提交是在 5 月。以下是我们正在积极推进的一些工作:
- 更完善的 CDP 覆盖。Kitesurf 实现了 CDP 协议的一个子集——足以满足大多数智能体和自动化工具的需求,包括可靠的 DOM 和网络检查——我们还在持续扩展其能力,力求尽可能完整。
- 渲染保真度,针对截图和 PDF,因为我们知道 LLM 往往看图比读底层文本效果更好。
- WPT 覆盖率。我们正在快速迭代,增加更多 Web API 并通过更多 WPT 测试,朝着让 Kitesurf 达到生产就绪的目标前进。
- 效率。我们让 CPU、内存和耗时基准测试一直保持运行,并与开发者平台的其他团队紧密合作,使 Kitesurf 尽可能经济高效。
结语
感谢你一路读到这里——我们知道这是一篇又长又偏技术的博文,但希望它还算有趣。我们之所以讲得这么详细,是因为我们深知构建一个新浏览器有多重要,也有多复杂,即便它只针对非常特定的场景。
Kitesurf 尚处于早期阶段,但我们希望尽快向你开放,并从你的反馈中学习。团队会积极改进它,频繁发布更新,重点关注性能、效率和兼容性。
最后一件事:等准备就绪后,我们会开源 Kitesurf,希望就在不久之后。我们的目标是让任何客户都能在自己的账户上部署属于自己的 Kitesurf 版本,如果他们愿意的话。
所以,来 playground 里试试吧,关注我们的 changelog,也欢迎到 Discord 上和团队聊聊。分享你的使用体验,给我们反馈,我们会认真倾听。
