Build1 publisher3 min readPublished Updated
Cloudflare's Python Workers GA hands WebAssembly wheel builds to package maintainers
Cloudflare took Python Workers to general availability two years after preview, built on PEP 783, a WebAssembly packaging standard it proposed. Native-package support now depends on upstream maintainers building those wheels and keeping urllib3's fetch path working.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened
- Database drivers such as asyncpg run unchanged because Cloudflare implemented the sandbox's stubbed POSIX socket syscalls over the Workers connect API.
- urllib3, the library under requests, keeps its Emscripten backend experimental and explicitly out of scope in its security policy.
- Wasmer, a competitor, published a benchmark with a minimal Python app starting in about 60ms on Wasmer Edge and about 900ms on Workers.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision Whether a native-extension package runs on Workers now depends on its maintainer's release process, so teams have to check each dependency for a PyEmscripten wheel.
- exposure Teams sending HTTP through requests on Workers rely on a backend urllib3 excludes from its security policy, so fetch-specific bugs like CVE-2025-50182's redirect failure sit outside that coverage.
- cost Latency-sensitive teams have to run their own p50 and p95 cold-start measurements, because the public figures are a roughly one-second Cloudflare number and a rival's benchmark.
Two of the three pieces of engineering behind the release landed outside Cloudflare's own code [23]. Packaging has the longest reach. Pyodide runs Python inside WebAssembly, so a package with a C, C++ or Rust extension has to be cross-compiled before a Worker can import it [3]. Until now Cloudflare did that compiling and hosted the results, and that capped how many packages were available [4]. PEP 783 names the target PyEmscripten, and cibuildwheel can now build for it [5][6]. The limit moves from Cloudflare's build list to each maintainer's release process [6]. InfoQ's report does not say how many maintainers have added the target.
The database work is the cleanest of the three. asyncpg and aiomysql connect through the standard library socket module, whose POSIX syscalls are stubs inside the WebAssembly sandbox [7]. Cloudflare implemented those syscalls over the Workers connect API [8]. The translation sits at the syscall layer, so the drivers run unmodified and Hyperdrive works on top of them [8]. One shim under the socket module covers every driver built on it, and no driver needs a Workers-specific patch [8].
HTTP went through upstream libraries instead. requests and httpx now route through the JavaScript fetch API in WebAssembly environments, and that routing lets the OpenAI, LangChain and MCP clients run inside a Worker [9]. For requests, the path runs through urllib3 [10]. illia-v, a urllib3 maintainer, wrote on Hacker News that the project merged large contributions for Pyodide and Emscripten support and later JSPI support, and that the funding went to the contributor who wrote them [10]. "There is a meaningful difference between funding a contribution to an upstream project and funding the upstream maintainers," he wrote [11]. urllib3 still labels the Emscripten backend experimental and lists it as out of scope in its security policy [12]. He cited CVE-2025-50182, in which redirect controls did not behave as expected once requests went through fetch, and warned there may be more differences because browser networking semantics differ from urllib3's usual backend [13].
The cold-start figures come mostly from a competitor. "It will be quite hard for them to achieve <100ms startup time with their current architecture," said Syrus Akbary, founder of Wasmer, which sells a competing WebAssembly platform [14]. Wasmer's benchmark from earlier this year put a minimal Python application at about 60ms on Wasmer Edge and about 900ms on Workers [15]. That is a 15-fold gap [1]. For the ratio to hold in production, the app would have to be as small as the benchmark's, and a real share of its requests would have to land on a cold start. "Our memory snapshot implementation has improved the cold starts significantly already," said Dominik Picheta, one of the Cloudflare post's authors [16]. He added that sharding reduces how often cold starts occur [17]. The earlier post he pointed to reported about 1.027 seconds, and Akbary asked whether it had been remeasured [18]. He also asked for current p50 and p95 figures with and without native packages [19].
The Python version is tied to runtime features. Compatibility flags select Python 3.12, 3.13 or 3.14, and older versions bring older Pyodide releases with fewer features, JSPI among them [20]. Akbary said this confirmed his objection, since the flag moves workerd too [21]. JSPI is also on illia-v's list of the urllib3 work behind requests [10]. A team that pins an older Python is choosing a runtime without a feature its HTTP client was adapted to use [20][10].
I think that for a Worker that mostly calls model APIs over HTTP, the fetch route and the packaging standard are enough to build on. For a latency-bound path that imports native packages, the public cold-start evidence is one earlier Cloudflare measurement and one competitor's benchmark [18][15].
What to watch
- Cloudflare publishing current p50 and p95 cold-start figures with and without native packages, as Akbary requested.
- urllib3 moving its Emscripten backend out of experimental status or bringing it into its security policy scope.
- Widely used native-extension packages adding PyEmscripten targets to their cibuildwheel builds.