Compatibility Dates
Understanding and managing Cloudflare compatibility dates
What Are Compatibility Dates?
The compatibility_date in wrangler.toml pins your Worker to a specific version of the Cloudflare Workers runtime. This prevents breaking changes from affecting your deployed code.
compatibility_date = "2024-12-01"How They Work
Cloudflare introduces runtime changes behind flags tied to dates
Setting a date enables all changes up to and including that date
Your code uses the same runtime behavior until you update the date
New projects should use a recent date
When to Update
Update the compatibility date when:
You are actively working on the project
You want access to new runtime features
The Cloudflare docs recommend a minimum date for a feature you need
Warning
Updating the compatibility date can change runtime behavior. Test thoroughly after updating, especially if jumping across many months.
Compatibility Flags
For granular control, you can enable or disable specific flags:
compatibility_date = "2024-12-01"
compatibility_flags = ["nodejs_compat"]Common flags:
| Flag | Description |
|---|---|
nodejs_compat | Enable Node.js built-in module support |
streams_enable_constructors | Enable constructable Streams API |
When You Actually Need nodejs_compat
nodejs_compat is not optional decoration. If your bundle (or an adapter such as zfb's _worker.js wrapper) imports a Node.js built-in like node:async_hooks (AsyncLocalStorage), node:crypto, or node:buffer, then omitting the flag makes wrangler refuse to load the worker. That is a deploy-time failure, not a graceful runtime fallback — the deploy stops; you do not get a working worker that silently degrades.
compatibility_date = "2024-12-01"
compatibility_flags = ["nodejs_compat"]The concrete trigger is the AsyncLocalStorage per-request-context pattern used by the SSR bindings adapter recipe: it threads Cloudflare bindings (KV, D1, R2, env) into a getCloudflareContext()-style accessor via node:async_hooks. The moment that import lands in your bundle, nodejs_compat becomes a hard requirement for every deploy of that worker.
Warning
This is a build/deploy-time gate, not a runtime one. You will not discover it from a passing local test that never imports the Node built-in — you discover it when wrangler refuses to load the worker during deployment. If a worker depends on node:* modules, keep nodejs_compat in its compatibility_flags permanently.
A Pin's Guarantee Depends on Your Installed Wrangler
compatibility_date pins runtime behavior, but the code that implements that behavior ships inside workerd, bundled with whatever wrangler version is installed locally. wrangler dev runs your pinned date against that bundled workerd -- it is not a live fetch of "what Cloudflare's edge looked like on this date." If the installed wrangler is old, local dev is simulating an old workerd's idea of your compatibility date, which can silently drift from what the current production edge actually does for the same pin.
The pin is not a one-time guarantee once set -- it only protects the code path you actually re-verify. Re-run wrangler dev (or a preview deploy) after any wrangler upgrade, not just after a compatibility_date change, and periodically re-check even when nothing in the config file moved.
Bump wrangler before you trust a long-standing pin
A compatibility_date that has not changed in months can still behave differently locally than it used to, if wrangler itself was upgraded (or fell behind) in that time. Re-verify against wrangler dev after any wrangler version bump, not only after editing compatibility_date.