Building a Browser Without V8: What Broke, and What Worked

· Medium ·

4 min read Original article ↗

Hideaki Takahashi

While V8 has dominated JavaScript execution for decades, we tried building a practical browser around Boa, a JavaScript engine written in Rust.

Press enter or click to view image in full size

V8, Google’s JavaScript engine, has become the dominant choice for practical browsers and many embedded runtimes. However, it has also been one of the main source of browser vulnerabilities. Typically, many involve memory-safety problems, such as CVE-2026–78910, a buffer overflow that could be triggered by a malicious HTML page.

Many people then are trying to implement a JavaScript engine in memory safe language such as Rust. One example is Boa, a JavaScript engine written in Rust. While Rust does not completely eliminate memory bugs, especially when unsafe code is involved, it still removes large classes of them by default.

For example, Cloudflare recently released Kitesurf, a new browser built largely with Rust components. Yet its main page scripts still run in V8 isolates. Boa is used only for the occasional eval() that Cloudflare Workers cannot execute natively.

This question has been particularly interesting to me because I have been developing h5i, a CLI-based red-teaming browser that treats both page automation and HTTP traffic capture as first-class capabilities.

Security teams routinely open unknown or deliberately malicious web pages. For that use case, the safety of the browser itself matters a lot. I therefore decided to use Boa as h5i’s primary JavaScript engine rather than embedding V8.

So, did it work? Yes, but only after sharply defining what “a practical browser” meant.げn

What worked

Boa is capable enough to run JavaScript used by many ordinary pages. In h5i, scripts can modify the DOM, register event handlers, use promises and timers, call fetch(), load ES modules, and communicate through WebSockets and EventSource. Forms, redirects, cookies, and script-triggered navigation also work.

This was enough to let h5i interact with pages whose contents are created or changed by JavaScript, maintain a session, click elements, submit forms, and inspect the resulting page and network traffic.

Boa also gives us a big resource efficiency. In our simple benchmark, h5i loaded a 22 KB documentation page in 59 ms with 65.6 MB of peak memory. Chromium’s headless_shell took 176 ms and 461.5 MB on the same test.

Removing V8 also means removing its JIT compiler from the attack surface. That is relevant for a browser designed to process hostile pages. It does not prove that h5i is secure, but it avoids one of the most complex and historically vulnerable components found in mainstream browsers.

Web compatibility was better than I initially expected. h5i currently passes 88,199 of 116,471 subtests, or 75.7%, in its selected core Web Platform Tests (WPT) suite.

What broke

However, we also encounter various problems.

For example, Boa does not have V8’s optimizing JIT, and can be slow in a large script-heavy page. In our script-built test page, h5i took 248 ms while Chromium headless_shell took 176 ms.

The second problem was compatibility. Running JavaScript is not the same as running the modern Web. Applications assume the presence of an enormous collection of browser APIs surrounding the engine. h5i supports the subset needed by the pages we currently target, but it has not yet cleared a production React application.

As a result, h5i works best for content-heavy sites, common web interactions, scraping, and HTTP-oriented security testing. However, highly interactive applications may still require Chromium.

Rust did not automatically make the browser secure

Using Rust and removing V8 reduced memory corruption in the JavaScript engine and JIT, although it did not solve browser security as a whole.

For instance, after implementing fetch(), we found a policy bug involving loopback access. A hostile page could request a local development service, read the response through JavaScript, and send it to another allowed destination. Although the code was memory-safe, the security model was wrong. We fixed it by checking both the destination and the document that initiated the request.

Was it worth it?

For h5i, yes.

The goal of h5i was not to replace Chrome for everyday browsing, but to build a lightweight browser for automated and security-sensitive red-teaming tasks, where lower memory use, a smaller attack surface, and control over the full browser stack can matter more than perfect compatibility.

Boa was practical for that scope, because it runs enough JavaScript to make h5i a real interactive browser, while keeping the project in Rust and avoiding a V8 dependency.

The answer would be different for a general-purpose browser. If the requirement is to run every major web application exactly as Chrome does, using V8 or another mature engine such as SpiderMonkey or JavaScriptCore remains the realistic choice.

Reference