Cloudflare Workers Gets a More Node.js-Like Module System
Cloudflare has rewritten the Workers module registry to behave more like Node.js and modern JavaScript runtimes, adding URL-based resolution, lazy compilation and stronger module compatibility.
On this page
Cloudflare is changing a part of Workers that most developers rarely see but that determines whether JavaScript modules behave as expected once they reach the server. On September 9, 2026, the company announced a rewritten module registry for workerd, its open-source Workers runtime, with URL-based resolution, better Node.js compatibility, lazy compilation, and shared module caching. The change is available now behind the new_module_registry compatibility flag, so it is an opt-in change rather than an immediate replacement for every deployed Worker.
Cloudflare Workers is fixing the layer between your code and the runtime
Cloudflare Workers already supports many Node.js runtime APIs, but API compatibility is only one part of running Node.js-oriented applications. A runtime also has to decide what an import points to, how that module is loaded, when it is compiled, and whether two references should produce the same module instance. That work is handled by the module registry, which sits underneath the application code. Cloudflare says the old implementation treated module references more like filesystem paths than URLs, creating differences from Node.js and browser behavior.
That distinction becomes visible when an application uses modern JavaScript features such as import.meta.resolve(). The API resolves a module specifier to the URL that would be used for an import, without actually loading the module, and is already widely available in browsers. Cloudflare's previous registry could not provide the same behavior inside Workers, which meant code that relied on normal module-resolution semantics could fail after deployment even when the JavaScript itself was valid.
The new module registry follows URL-based resolution
The rewritten registry changes that foundation by treating module specifiers as real URLs. Relative imports therefore follow URL resolution rules, while query strings and fragments can participate in module identity rather than being treated as meaningless text. Cloudflare also says node: built-in modules now resolve to the same module instance regardless of how they are reached. For developers, that matters because module-loading behavior becomes closer to what modern JavaScript tooling already expects instead of requiring a special Cloudflare interpretation.
Cloudflare has also added support for import.meta.url, import.meta.main, and import.meta.resolve() in the new registry. import.meta.url identifies the current module, while import.meta.resolve() lets code calculate where another module would resolve without importing it. These features are useful in applications that need to locate assets, load modules dynamically, or behave differently depending on which file is acting as the entry point.
CommonJS and ECMAScript modules now meet more cleanly
One of the harder compatibility problems is the boundary between CommonJS and ECMAScript modules. CommonJS is the older Node.js module system built around require(), while ECMAScript modules use import and export. The new Workers registry follows Node.js behavior when require() encounters an ECMAScript module, including the special module.exports export convention. It also refuses to synchronously require a module whose dependency graph contains top-level await, because there is no finished value for synchronous require() to return.
That last restriction is easy to overlook but important for real applications. A developer can use import() when asynchronous module evaluation is required, while synchronous require() remains limited to modules that can finish evaluation immediately. Aligning this behavior with Node.js reduces one more place where an application can behave differently after moving from a traditional Node.js environment to a Worker.
Import attributes and WebAssembly get stricter behavior
The new registry also changes how import attributes are handled. An import such as import data from './config.json' with { type: 'json' } is validated rather than having its attributes silently ignored. Cloudflare currently enables the JSON type, while unsupported types such as text and bytes produce explicit errors instead of quietly proceeding. That is a small implementation detail with a useful consequence: invalid module assumptions are exposed during development instead of being hidden by permissive runtime behavior.
WebAssembly, the binary instruction format commonly used when applications need near-native execution for selected workloads, also gains source-phase imports. A Worker can obtain a WebAssembly module before instantiating it and then pass that module to the WebAssembly APIs. Cloudflare says this behavior follows the direction used by Node.js and other runtimes, although source-phase imports currently apply to WebAssembly rather than every module type.
Lazy compilation changes how Workers prepare modules
The old registry compiled an entire Worker bundle up front, including modules that might never be used. Cloudflare says it also kept separate copies across its V8 isolates, the lightweight execution environments used to run Workers, which could mean the same source was compiled more than once. The new registry instead compiles modules when they are first imported, whether the import is static or dynamic, and is designed around shared caching.
That does not automatically mean every Worker becomes faster. Cloudflare has not published a general benchmark showing a fixed percentage improvement for application startup or request latency, and developers should not turn the implementation details into a performance guarantee. The more defensible benefit is architectural: modules that are never used do not need to be compiled simply because they exist in the application's graph, while repeated work can be reduced through shared caches.
Vite makes the change more relevant to modern web apps
The timing also matters because modern Workers projects are increasingly built with tools that preserve more of an application's module graph. Cloudflare says its Vite plugin uses Vite 8 and Rolldown to resolve imports, convert CommonJS to ECMAScript modules where necessary, and produce an entry module plus additional chunks for code splitting. Vite 8 itself moved to Rolldown as its unified Rust-based bundler, replacing the earlier split between esbuild and Rollup.
That creates a useful connection between the build system and the runtime. A traditional bundler can hide many module-resolution differences by turning an application into one large output file, but a runtime that understands modules directly can preserve more of the structure produced by modern tooling. Cloudflare says the new registry gives bundlers such as Rolldown room to perform fewer transformations and lets the runtime take more responsibility for module resolution.
Cloudflare's 64 MiB Worker limit removes another deployment constraint
The module-registry work arrives alongside a separate Workers change that is easy to miss but relevant to larger applications. On September 4, Cloudflare removed its compressed bundle-size limits of 3 MB on the Free plan and 10 MB on paid plans. Workers now checks the uncompressed bundle size instead, with a 64 MiB ceiling across plans. That gives applications more room for larger dependency trees and heavier frameworks, although the size change should not be confused with the module-registry rewrite itself.
Together, the changes point in the same direction: Cloudflare is making Workers less dependent on aggressive bundling and more capable of handling applications whose structure resembles a conventional JavaScript project. The 64 MiB limit gives developers more room to deploy; the module registry determines how the resulting modules are resolved and executed. Neither change turns Workers into a general-purpose Node.js server, but the boundary between the two environments is becoming less awkward.
The new registry is still opt-in
There is an important qualification for anyone maintaining an existing application. Cloudflare has not made the new registry the default compatibility behavior, and the company says deployed Workers continue using the existing implementation unless developers explicitly enable new_module_registry. That gives teams a way to test their applications before changing the module-resolution behavior underneath production code.
The practical test is straightforward: enable the flag in a development or staging Worker, run the application's normal test suite, and pay particular attention to dynamic imports, CommonJS packages, JSON imports, WebAssembly modules, and code that depends on module URLs. If the application works correctly, the new registry provides a more standards-aligned foundation without requiring a rewrite of the application itself. Cloudflare has not yet assigned a compatibility date that turns the feature on automatically, so the next major step will be deciding when the new implementation is ready to become the normal path.
Written by


