CLOUDFLARE WORKERS
Cloudflare の Kitesurf を、公開情報だけで作ってみた
Chromium を使わず、Rust で書いたブラウザエンジンを Wasm にして Workers の isolate で動かす。Cloudflare が 2026 年 8 月に発表した Kitesurf と同じ構成を、発表記事に名前が挙がっている部品だけで組んだものです。ソースコードは非公開なので、中身は見ていません。
以下の画像は全部、この Worker が Wasm の中で描きました。
3 つのブラウザで同じページを撮る
左が本物のブラウザ (Chromium)、中央が Cloudflare の 本物の Kitesurf、右がこの記事で組んだ疑似 Kitesurfです。3 枚は同じ時刻に撮っています — ja.wikipedia のトップページは日ごとに変わるので、別々に撮るとエンジンの違いとページの違いが混ざります。画像を押すと元の大きさで開きます。
撮影 2026-09-08 07:44 UTC。下の「自分で試す」だけはその場で描きます。
ja.wikipedia.org
日本語の見出しと本文、2 カラム、写真 24 枚。CSS は 3 枚で 231 KB
https://ja.wikipedia.org/wiki/メインページ · 1000×780
Chromium202 KB
本物のブラウザ。Browser Run の既定
課金 1266 ms
Kitesurf390 KB
Cloudflare 版。Chromium を使わない
課金 5542 ms
疑似 Kitesurf359 KB
公開情報だけを見て組んだもの (browser-on-workers)
2 パス · CSS 3 枚 · 画像 24 枚
developer.mozilla.org
CSS 20 枚。本文中の map() が等幅で出ている。JS は 938 KB 実行してエラー 0 件
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map · 1000×780
Chromium54 KB
本物のブラウザ。Browser Run の既定
課金 743 ms
Kitesurf66 KB
Cloudflare 版。Chromium を使わない
課金 3985 ms
疑似 Kitesurf57 KB
公開情報だけを見て組んだもの (browser-on-workers)
2 パス · CSS 18 枚 · 画像 0 枚
react.dev
SSG。ページの JS を動かすと React のハイドレーションが本文を消すので、JS を切って描き直したもの
https://react.dev/ · 1000×780
Chromium153 KB
本物のブラウザ。Browser Run の既定
課金 733 ms
Kitesurf84 KB
Cloudflare 版。Chromium を使わない
課金 6715 ms
疑似 Kitesurf67 KB
公開情報だけを見て組んだもの (browser-on-workers)
2 パス · CSS 1 枚 · 画像 24 枚
todomvc.com (React の SPA)
HTML の中は空。見えているものは全部、ページの JS が描いたもの
https://todomvc.com/examples/react/dist/ · 1000×780
Chromium22 KB
本物のブラウザ。Browser Run の既定
課金 1329 ms
Kitesurf24 KB
Cloudflare 版。Chromium を使わない
課金 2411 ms
疑似 Kitesurf20 KB
公開情報だけを見て組んだもの (browser-on-workers)
2 パス · CSS 1 枚 · 画像 0 枚
自分で試す
好きな URL を入れてください。この場に画像が出ます。
外部 CSS と画像は Worker が取ってきてエンジンに渡します。重いページは 10 秒ほどかかります。10 秒で 4 枚までにしています。
JavaScript も動く
ページの <script> を Boa (Rust で書かれた JavaScript エンジン) で実行しています。Workers は eval と new Function を禁じていますが、Boa が自分でコンパイルするので、ページの中の eval は通ります。
同じページを左右に並べます。左はあなたのブラウザが表示したもの、右はこの Worker が Wasm の中で描いた画像です。緑の枠はどちらもページの <script> が書き換えたものです。
あなたのブラウザiframe · 760×560
6 つとも緑になります — 最後の枠の 5000 万回のループも、数十ミリ秒で終わるので
疑似 Kitesurf が描いた画像PNG · 760×560
最後の枠だけ灰色のまま — Boa はループ 50 万回で JavaScript を止めます。止まる前に書いた 5 つは残ります
違いは最後の枠だけです。ページを単体で開くこともできます。
V8 と Boa が同じ isolate に同居する形になります。Worker のコードは V8 で動き、ページのコードは Wasm の中の Boa で動く。Cloudflare の Kitesurf も同じ構造です。
中身
| 役割 | 使っているもの |
| HTML のパース | html5ever |
| DOM | blitz-dom |
| CSS | Stylo — Firefox の CSS エンジン |
| レイアウト | Taffy |
| テキスト整形 | Parley |
| 描画 | blitz-paint + vello_cpu |
| JavaScript | Boa (blitz-vibey-script 経由) |
| PNG 化 | 自前。Workers に画像の API が無いので |
| フォント | Static Assets に置いて起動時に読む |
| 外向きの取得 | Worker の fetch() 1 箇所だけ |
Rust を wasm32-unknown-unknown にビルドして Worker から呼んでいます。Wasm は 15 MB ほど。
できないこと
- フォントに入れた文字しか出ません。いまは Latin と、ひらがな・カタカナ・漢字
- ページの JavaScript には実行上限があります。
while (true) はループ 50 万回で止まります。Boa には実行を中断する仕組みが無いので、上限を自分で積んでいます
- 展開すると大きすぎる画像は飛ばします。1 枚で 87 MB になる画像があり、isolate のメモリに載りません
- SSR / SSG のページで React のハイドレーションが通りません。 描き直しに落ちて本文が消えるので、そのときは JS を切って描き直しています。SPA (クライアントで描く作り) は動きます
- Web Workers と WebAssembly をページに出していません。regex101 はそれを見て自分で「非対応」と表示します (
Worker=false, Promise=true, WASM=false)
- ページからの
fetch() は必ず失敗します。データを取ってから描くページは空のままになります
- 動くもの (アニメーション、動画、WebGL) は扱いません
- レイアウトが返ってこないページがまだあります