Kitesurf:面向 AI 代理的 Worker 原生浏览器
我们要不要自研浏览器?
多年来,这个问题每隔几个月就会在Cloudflare内部被提起。不出所料,每次都会引发长篇讨论,各方罗列种种理由,力陈自研的必要性。浏览器无疑是我们日常电脑使用中最重要的软件,甚至可以说是“互联网的操作系统”。我们的使命是助力构建更美好的互联网——面对打造全新浏览器的挑战,谁能不动心?
但长期以来,我们始终没能在这项工程的技术难度,与它能解决的独特问题之间找到平衡。于是,这个想法一次次被搁置,直到现在。
奇妙的转折点出现了:一方面,我们开发者平台的一系列核心技术取得突破性进展;另一方面,AI代理的兴起让新型浏览器的需求变得极为迫切。
如今,在Workers中运行WebAssembly (Wasm,网页汇编)的技术已十分成熟。动态Workers、基于SQLite的Durable Objects、Worker间远程过程调用(RPC)、高级服务绑定以及增强型NodeJS兼容性等基础能力,为开发以往无法实现的复杂应用打开了大门。
随着AI的崛起,我们的无头浏览器自动化API产品Browser Run迎来爆发式增长。AI代理执行多数任务都离不开浏览器,甚至可以说没有浏览器就无法完成。
但问题随之而来:Chromium这类浏览器引擎是为人类用户设计的,而非AI代理,自带大量AI模型完全不需要的冗余功能。它们占用的内存和计算资源极高,给每个代理都分配独立实例的成本高得惊人。这导致大部分网络资源只能被参数规模大、成本高昂的顶尖AI模型使用,大量其他代理应用被拒之门外。
我们应该为所有AI代理打造一款真正适配其需求的浏览器——哪怕它弱化了只对人类有用的功能。具体来说:
- AI不在乎标签页、主题、浏览器扩展或跨设备同步,它关心的是令牌数量、上下文窗口、可扩展性、性能与成本。
- 结构化的机器可读内容很重要,但视觉完美度、每秒60帧的流畅滚动毫无意义。哪怕CSS解析略有偏差、渲染达不到像素级精准,AI代理也能正常工作。
- AI使用浏览器时的威胁模型截然不同,提示注入、工具安全等新问题才是重中之重。
基于这些认知,12周前我们再次提出那个问题:要不要自研浏览器?这次的答案毫无分歧:要!
今天,我们正式宣布Kitesurf——一款专为AI代理打造、完全基于Workers运行的全新浏览器。目前它处于Beta测试阶段,可在Browser Run中免费使用。
在截图、HTML提取等AI代理常见任务中,Kitesurf的CPU和内存消耗远低于Chromium。接下来,我们将讲述它的开发历程。做好准备,内容会偏技术向,但我们保证足够有趣。
项目起源
和Cloudflare的许多优秀项目一样,Kitesurf始于一个“无心插柳”的想法:有人发现了一个有趣的东西,随即用一个看似不可能却极具吸引力的点子,“勾住”了整个团队的注意力。
我们最初的灵感来自obscura——一款用Rust编写的、专为AI自动化设计的无头引擎,主打“无需Chrome、无需Node.js、无依赖”。

随后,我们尝试借助AI代理将它移植到Workers平台。一开始效果很差,但当我们给AI制定了清晰的方案和成功标准——详细到让它可以不断循环、按需提问——移植终于成功了。
这个(勉强)可行的原型让我们大为震撼,于是决定让团队全力投入开发。
核心设计决策
正式开发前,我们确定了几项关键设计原则:
测试先行,反复测试
我们深知,从原型到能在生产环境大规模落地的实用浏览器,需要大量工作和迭代。借助AI加速开发是关键,但如何在如此复杂的项目中用AI,既保证代码和结果质量,又不拖慢进度?答案是尽可能丰富测试用例。
Web Platform Tests (WPT,网页平台测试)正是理想选择:这套全面的测试套件为AI代理提供了明确的功能合规性评估标准。我们筛选并排序需要实现的功能,分配给AI代理开发,而人类工程师则专注于架构设计和审核AI的实现方案。
不过WPT测试有其局限性:它只衡量对W3C标准的合规性,无法验证浏览器渲染和交互真实网站的能力。为了弥补这一缺口,我们结合了集成测试与视觉回归测试——在真实网站上运行多步骤测试,同时对比Kitesurf与Chromium的执行断言,以及每一步的渲染输出,以此凸显两者间的差异。
优先使用Rust
Cloudflare在Workers中支持WebAssembly (Wasm)已有相当长的时间,这让我们可以使用高性能的C、C++和Rust包,并将它们编译为Wasm。但如果使用Emscripten及其多层模拟依赖,编译出的二进制文件会体积庞大且运行缓慢。
因此,我们尽可能选择原生Rust开发,并通过wasm-bindgen直接编译为WebAssembly,从而避开不必要的模拟层,实现更贴近底层的高效、可靠运行。
异常处理机制
浏览器必须能渲染整个不可靠甚至充满恶意的网络内容,且不能轻易崩溃。因此,异常处理绝非可有可无的细节,而是保障应用在不良输入下存活的核心机制。
我们从一开始就定下规则:任何故障都只能导致空白帧或缺失元素,绝不允许会话直接终止。在所有边界处捕获错误,默认返回安全的空值,同时记录足够信息以便排查问题。
隔离性设计
不同于在个人笔记本上运行浏览器(你访问的是信任的网站,不同网站间共享部分资源也可接受),AI代理会根据任务需求访问任意来源的任意代码。
因此,Kitesurf的开发基于一个假设:每次页面加载都是不可信输入,每个会话都从零开始。每个组件相互隔离,只能访问完成其功能严格必需的资源。

这与Cloudflare Workers“设计即隔离”的安全模型完美契合。不过平台仅提供了隔离环境的边界,我们还需在应用层面贯彻这一原则,明确每个组件的访问权限,确保资源不会跨页面泄露。
尽可能无状态
状态是导致故障成本高昂的根源——如果没有需要恢复的状态,崩溃后只需启动新实例并重放请求即可。无状态组件天生具备可丢弃、可并行的特性:一旦停滞就销毁,可同时运行上千个实例,根据需求动态调整规模,而非维持预热状态。这完全适配自动化场景:流量往往突发而来,最经济的方式就是按需启动任务,用完即销毁,仅按实际使用量计费。简言之,只要组件可以设计为无状态,就必须这么做。
开发实现过程
有了清晰的方案、全面的测试和完善的工具环境,我们得以突破初始原型,正式启动开发。以下是Kitesurf当前的请求处理流程概览:

接下来,我们深入解析构成Kitesurf的三大核心组件:引擎(Engine)、页面脚本(PageScript)和页面渲染器(PageRenderer)。
资源获取
要渲染不可信的网页,浏览器必须从互联网获取各类资源:图片、字体、CSS、JavaScript和Wasm文件。这是浏览器最危险的操作之一。
Kitesurf仅通过SandboxOutbound Worker这一个组件完成资源获取,其他组件均无法直接访问网络——这一限制由动态Workers机制强制执行。引擎通过它初始化页面,获取主文档及其脚本;PageScript则负责获取其他所有资源:样式表、图片、字体,以及页面自身发起的fetch()请求。
我们通过SandboxOutbound Worker执行CORS(跨域资源共享)规则、注入浏览器特征头、过滤响应,并为每个页面独立存储Cookie。任何不符合策略的请求都会返回403——每个组件仅能获得其所需的网络权限,不多不少。

引擎组件
引擎是Kitesurf唯一对外开放的组件。它处理Chrome DevTools Protocol (CDP)的WebSocket和HTTP REST API,提供供内部测试使用的着陆页,最重要的是,它负责存储每个会话的状态——其他所有组件都是无状态的。

使用CDP的优势在于客户端兼容性:Puppeteer、Playwright、chrome-remote-interface,甚至Chrome DevTools前端本身,都可以直接对接Kitesurf。这也是Browser Run的运行机制(后文会解释其重要性)。
尽管名为“引擎”,它却是Kitesurf中最简单的组件,真正的技术亮点在后面两个组件。
页面脚本组件
PageScript充分展现了Workers新功能的强大——尤其是动态Workers,没有它,Kitesurf根本无法实现。
以下是PageScript内部工作流程的简化示意图:

每个新页面或进程外iframe(OOPIF)都会通过动态Workers启动一个长期运行的PageScript隔离环境,负责处理页面会话,包含干净的全局对象(globalThis)和文档对象模型(DOM)。
DOM对象会通过解析HTML文档、执行所有JavaScript脚本逐步构建完成。HTML和CSS解析分别使用模块化渲染引擎Blitz的部分组件,以及Firefox的高性能CSS解析器Stylo——两者均由Rust编写。
每个