zudo-cloudflare-wisdom
GitHub repository

Type to search...

to open search from anywhere

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:

FlagDescription
nodejs_compatEnable Node.js built-in module support
streams_enable_constructorsEnable 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.

Revision History

CreatedUpdated