Skip to content

Changelog

New updates and improvements at Cloudflare.

Use the latest JavaScript features with Wrangler CLI v4

We've released the next major version of Wrangler, the CLI for Cloudflare Workers — wrangler@4.0.0. Wrangler v4 is a major release focused on updates to underlying systems and dependencies, along with improvements to keep Wrangler commands consistent and clear.

You can run the following command to install it in your projects:

npm i wrangler@latest

Unlike previous major versions of Wrangler, which were foundational rewrites ↗︎ and rearchitectures ↗︎ — Version 4 of Wrangler includes a much smaller set of changes. If you use Wrangler today, your workflow is very unlikely to change.

A detailed migration guide is available and if you find a bug or hit a roadblock when upgrading to Wrangler v4, open an issue on the cloudflare/workers-sdk repository on GitHub ↗︎.

Going forward, we'll continue supporting Wrangler v3 with bug fixes and security updates until Q1 2026, and with critical security updates until Q1 2027, at which point it will be out of support.

Access your Worker's environment variables from process.env

You can now access environment variables and secrets on process.env when using the nodejs_compat compatibility flag.

const apiClient = ApiClient.new({ apiKey: process.env.API_KEY });
const LOG_LEVEL = process.env.LOG_LEVEL || "info";

In Node.js, environment variables are exposed via the global process.env object. Some libraries assume that this object will be populated, and many developers may be used to accessing variables in this way.

Previously, the process.env object was always empty unless written to in Worker code. This could cause unexpected errors or friction when developing Workers using code previously written for Node.js.

Now, environment variables, secrets, and version metadata can all be accessed on process.env.

To opt-in to the new process.env behaviour now, add the nodejs_compat_populate_process_env compatibility flag to your wrangler.json configuration:

{
	// Rest of your configuration
	// Add "nodejs_compat_populate_process_env" to your compatibility_flags array
	"compatibility_flags": ["nodejs_compat", "nodejs_compat_populate_process_env"],
	// Rest of your configuration
compatibility_flags = [ "nodejs_compat", "nodejs_compat_populate_process_env" ]

After April 1, 2025, populating process.env will become the default behavior when both nodejs_compat is enabled and your Worker's compatibility_date is after "2025-04-01".

Introducing Media Transformations from Cloudflare Stream

Today, we are thrilled to announce Media Transformations, a new service that brings the magic of Image Transformations to short-form video files, wherever they are stored!

For customers with a huge volume of short video — generative AI output, e-commerce product videos, social media clips, or short marketing content — uploading those assets to Stream is not always practical. Sometimes, the greatest friction to getting started was the thought of all that migrating. Customers want a simpler solution that retains their current storage strategy to deliver small, optimized MP4 files. Now you can do that with Media Transformations.

To transform a video or image, enable transformations for your zone, then make a simple request with a specially formatted URL. The result is an MP4 that can be used in an HTML video element without a player library. If your zone already has Image Transformations enabled, then it is ready to optimize videos with Media Transformations, too.

URL formattext
https://example.com/cdn-cgi/media/<OPTIONS>/<SOURCE-VIDEO>

For example, we have a short video of the mobile in Austin's office. The original is nearly 30 megabytes and wider than necessary for this layout. Consider a simple width adjustment:

Example URLtext
https://example.com/cdn-cgi/media/width=640/<SOURCE-VIDEO>
https://developers.cloudflare.com/cdn-cgi/media/width=640/https://middlecache.ced.cloudflare.com/v1/aus-mobile/aus-mobile.mp4

The result is less than 3 megabytes, properly sized, and delivered dynamically so that customers do not have to manage the creation and storage of these transformed assets.

For more information, learn about Transforming Videos.

Use the latest JavaScript features with Wrangler CLI v4.0.0-rc.0

We've released a release candidate of the next major version of Wrangler, the CLI for Cloudflare Workers — wrangler@4.0.0-rc.0.

You can run the following command to install it and be one of the first to try it out:

npm i wrangler@v4-rc

Unlike previous major versions of Wrangler, which were foundational rewrites ↗︎ and rearchitectures ↗︎ — Version 4 of Wrangler includes a much smaller set of changes. If you use Wrangler today, your workflow is very unlikely to change. Before we release Wrangler v4 and advance past the release candidate stage, we'll share a detailed migration guide in the Workers developer docs. But for the vast majority of cases, you won't need to do anything to migrate — things will just work as they do today. We are sharing this release candidate in advance of the official release of v4, so that you can try it out early and share feedback.

New JavaScript language features that you can now use with Wrangler v4

Version 4 of Wrangler updates the version of esbuild ↗︎ that Wrangler uses internally, allowing you to use modern JavaScript language features, including:

The using keyword from Explicit Resource Management

The using keyword from the Explicit Resource Management standard makes it easier to work with the JavaScript-native RPC system built into Workers. This means that when you obtain a stub, you can ensure that it is automatically disposed when you exit scope it was created in:

function sendEmail(id, message) {
  using user = await env.USER_SERVICE.findUser(id);
  await user.sendEmail(message);

  // user[Symbol.dispose]() is implicitly called at the end of the scope.
}
Import attributes

Import attributes ↗︎ allow you to denote the type or other attributes of the module that your code imports. For example, you can import a JSON module, using the following syntax:

import data from "./data.json" with { type: "json" };

Other changes

--local is now the default for all CLI commands

All commands that access resources (for example, wrangler kv, wrangler r2, wrangler d1) now access local datastores by default, ensuring consistent behavior.

Clearer policy for the minimum required version of Node.js required to run Wrangler

Moving forward, the active, maintenance, and current versions of Node.js ↗︎ will be officially supported by Wrangler. This means the minimum officially supported version of Node.js you must have installed for Wrangler v4 will be Node.js v18 or later. This policy mirrors how many other packages and CLIs support older versions of Node.js, and ensures that as long as you are using a version of Node.js that the Node.js project itself supports, this will be supported by Wrangler as well.

Features previously deprecated in Wrangler v3 are now removed in Wrangler v4

All previously deprecated features in Wrangler v2 ↗︎ and in Wrangler v3 ↗︎ have now been removed. Additionally, the following features that were deprecated during the Wrangler v3 release have been removed:

  • Legacy Assets (using wrangler dev/deploy --legacy-assets or the legacy_assets config file property). Instead, we recommend you migrate to Workers assets ↗︎.
  • Legacy Node.js compatibility (using wrangler dev/deploy --node-compat or the node_compat config file property). Instead, use the nodejs_compat compatibility flag ↗︎. This includes the functionality from legacy node_compat polyfills and natively implemented Node.js APIs.
  • wrangler version. Instead, use wrangler --version to check the current version of Wrangler.
  • getBindingsProxy() (via import { getBindingsProxy } from "wrangler"). Instead, use the getPlatformProxy() API ↗︎, which takes exactly the same arguments.
  • usage_model. This no longer has any effect, after the rollout of Workers Standard Pricing ↗︎.

We'd love your feedback! If you find a bug or hit a roadblock when upgrading to Wrangler v4, open an issue on the cloudflare/workers-sdk repository on GitHub ↗︎.

Bind the Images API to your Worker

You can now interact with the Images API directly in your Worker.

This allows more fine-grained control over transformation request flows and cache behavior. For example, you can resize, manipulate, and overlay images without requiring them to be accessible through a URL.

The Images binding can be configured in the Cloudflare dashboard for your Worker or in the Wrangler configuration file in your project's directory:

{
	"images": {
		"binding": "IMAGES", // i.e. available in your Worker on env.IMAGES
	},
}
[images]
binding = "IMAGES"

Within your Worker code, you can interact with this binding by using env.IMAGES.

Here's how you can rotate, resize, and blur an image, then output the image as AVIF:

const info = await env.IMAGES.info(stream);
// stream contains a valid image, and width/height is available on the info object

const response = (
	await env.IMAGES.input(stream)
		.transform({ rotate: 90 })
		.transform({ width: 128 })
		.transform({ blur: 20 })
		.output({ format: "image/avif" })
).response();

return response;

For more information, refer to Images Bindings.

Autofix Worker name configuration errors at build time

Auto-fixing Workers Name in Git Repo

Small misconfigurations shouldn’t break your deployments. Cloudflare is introducing automatic error detection and fixes in Workers Builds, identifying common issues in your wrangler.toml or wrangler.jsonc and proactively offering fixes, so you spend less time debugging and more time shipping.

Here's how it works:

  1. Before running your build, Cloudflare checks your Worker's Wrangler configuration file (wrangler.toml or wrangler.jsonc) for common errors.
  2. Once you submit a build, if Cloudflare finds an error it can fix, it will submit a pull request to your repository that fixes it.
  3. Once you merge this pull request, Cloudflare will run another build.

We're starting with fixing name mismatches between your Wrangler file and the Cloudflare dashboard, a top cause of build failures.

This is just the beginning, we want your feedback on what other errors we should catch and fix next. Let us know in the Cloudflare Developers Discord, #workers-and-pages-feature-suggestions ↗︎.

Rewind, Replay, Resume: Introducing DVR for Stream Live

Previously, all viewers watched "the live edge," or the latest content of the broadcast, synchronously. If a viewer paused for more than a few seconds, the player would automatically "catch up" when playback started again. Seeking through the broadcast was only available once the recording was available after it concluded.

Starting today, customers can make a small adjustment to the player embed or manifest URL to enable the DVR experience for their viewers. By offering this feature as an opt-in adjustment, our customers are empowered to pick the best experiences for their applications.

When building a player embed code or manifest URL, just add dvrEnabled=true as a query parameter. There are some things to be aware of when using this option. For more information, refer to DVR for Live.

Create and deploy Workers from Git repositories

Import repo or choose template

You can now create a Worker by:

  • Importing a Git repository: Choose an existing Git repo on your GitHub/GitLab account and set up Workers Builds to deploy your Worker.
  • Deploying a template with Git: Choose from a brand new selection of production ready examples ↗︎ to help you get started with popular frameworks like Astro ↗︎, Remix ↗︎ and Next ↗︎ or build stateful applications with Cloudflare resources like D1 databases, Workers AI or Durable Objects! When you're ready to deploy, Cloudflare will set up your project by cloning the template to your GitHub/GitLab account, provisioning any required resources and deploying your Worker.

With every push to your chosen branch, Cloudflare will automatically build and deploy your Worker.

To get started, go to the Workers dashboard ↗︎.

These new features are available today in the Cloudflare dashboard to a subset of Cloudflare customers, and will be coming to all customers in the next few weeks. Don't see it in your dashboard, but want early access? Add your Cloudflare Account ID to this form ↗︎.

Revamped Workers Metrics

We've revamped the Workers Metrics dashboard ↗︎.

Workers Metrics dashboard

Now you can easily compare metrics across Worker versions, understand the current state of a gradual deployment, and review key Workers metrics in a single view. This new interface enables you to:

  • Drag-and-select using a graphical timepicker for precise metric selection.
Workers Metrics graphical timepicker
  • Use histograms to visualize cumulative metrics, allowing you to bucket and compare rates over time.
  • Focus on Worker versions by directly interacting with the version numbers in the legend.
Workers Metrics legend selector
  • Monitor and compare active gradual deployments.
  • Track error rates across versions with grouping both by version and by invocation status.
  • Measure how Smart Placement improves request duration.

Learn more about metrics.

Transform HTML quickly with streaming content

You can now transform HTML elements with streamed content using HTMLRewriter.

Methods like replace, append, and prepend now accept Response and ReadableStream values as Content.

This can be helpful in a variety of situations. For instance, you may have a Worker in front of an origin, and want to replace an element with content from a different source. Prior to this change, you would have to load all of the content from the upstream URL and convert it into a string before replacing the element. This slowed down overall response times.

Now, you can pass the Response object directly into the replace method, and HTMLRewriter will immediately start replacing the content as it is streamed in. This makes responses faster.

index.jsjs
class ElementRewriter {
	async element(element) {
		// able to replace elements while streaming content
		// the fetched body is not buffered into memory as part
		// of the replace
		let res = await fetch("https://upstream-content-provider.example");
		element.replace(res);
	}
}

export default {
	async fetch(request, env, ctx) {
		let response = await fetch("https://site-to-replace.com");
		return new HTMLRewriter()
			.on("[data-to-replace]", new ElementRewriter())
			.transform(response);
	},
};
index.tsts
class ElementRewriter {
	async element(element: any) {
		// able to replace elements while streaming content
		// the fetched body is not buffered into memory as part
		// of the replace
		let res = await fetch('https://upstream-content-provider.example');
		element.replace(res);
	}
}

export default {
	async fetch(request, env, ctx): Promise<Response> {
		let response = await fetch('https://site-to-replace.com');
		return new HTMLRewriter().on('[data-to-replace]', new ElementRewriter()).transform(response);
	},
} satisfies ExportedHandler<Env>;

For more information, see the HTMLRewriter documentation.

Support for Node.js DNS, Net, and Timer APIs in Workers

When using a Worker with the nodejs_compat compatibility flag enabled, you can now use the following Node.js APIs:

node:net

You can use node:net ↗︎ to create a direct connection to servers via a TCP sockets with net.Socket ↗︎.

index.jsjs
import net from "node:net";

const exampleIP = "127.0.0.1";

export default {
	async fetch(req) {
		const socket = new net.Socket();
		socket.connect(4000, exampleIP, function () {
			console.log("Connected");
		});

		socket.write("Hello, Server!");
		socket.end();

		return new Response("Wrote to server", { status: 200 });
	},
};
index.tsts
import net from "node:net";

const exampleIP = "127.0.0.1";

export default {
  async fetch(req): Promise<Response> {
    const socket = new net.Socket();
    socket.connect(4000, exampleIP, function () {
      console.log("Connected");
    });

    socket.write("Hello, Server!");
    socket.end();

    return new Response("Wrote to server", { status: 200 });
  },
} satisfies ExportedHandler;

Additionally, you can now use other APIs including net.BlockList ↗︎ and net.SocketAddress ↗︎.

Note that net.Server ↗︎ is not supported.

node:dns

You can use node:dns ↗︎ for name resolution via DNS over HTTPS using Cloudflare DNS ↗︎ at 1.1.1.1.

index.jsjs
import dns from "node:dns";

let response = await dns.promises.resolve4("cloudflare.com", "NS");
index.tsts
import dns from 'node:dns';

let response = await dns.promises.resolve4('cloudflare.com', 'NS');

All node:dns functions are available, except lookup, lookupService, and resolve which throw "Not implemented" errors when called.

node:timers

You can use node:timers ↗︎ to schedule functions to be called at some future period of time.

This includes setTimeout ↗︎ for calling a function after a delay, setInterval ↗︎ for calling a function repeatedly, and setImmediate ↗︎ for calling a function in the next iteration of the event loop.

index.jsjs
import timers from "node:timers";

console.log("first");
timers.setTimeout(() => {
	console.log("last");
}, 10);

timers.setTimeout(() => {
	console.log("next");
});
index.tsts
import timers from "node:timers";

console.log("first");
timers.setTimeout(() => {
  console.log("last");
}, 10);

timers.setTimeout(() => {
  console.log("next");
});

Faster Workers Builds with Build Caching and Watch Paths

Build caching settingsBuild watch path settings

Workers Builds, the integrated CI/CD system for Workers (currently in beta), now lets you cache artifacts across builds, speeding up build jobs by eliminating repeated work, such as downloading dependencies at the start of each build.

  • Build Caching: Cache dependencies and build outputs between builds with a shared project-wide cache, ensuring faster builds for the entire team.

  • Build Watch Paths: Define paths to include or exclude from the build process, ideal for monorepos to target only the files that need to be rebuilt per Workers project.

To get started, select your Worker on the Cloudflare dashboard ↗︎ then go to Settings > Builds, and connect a GitHub or GitLab repository. Once connected, you'll see options to configure Build Caching and Build Watch Paths.

Bypass caching for subrequests made from Cloudflare Workers, with Request.cache

You can now use the cache property of the Request interface to bypass Cloudflare's cache when making subrequests from Cloudflare Workers, by setting its value to no-store.

index.jsjs
export default {
	async fetch(req, env, ctx) {
		const request = new Request("https://cloudflare.com", {
			cache: "no-store",
		});
		const response = await fetch(request);
		return response;
	},
};
index.tsts
export default {
  async fetch(req, env, ctx): Promise<Response> {
		const request = new Request("https://cloudflare.com", { cache: 'no-store'});
		const response = await fetch(request);
    return response;
  }
} satisfies ExportedHandler<Environment>

When you set the value to no-store on a subrequest made from a Worker, the Cloudflare Workers runtime will not check whether a match exists in the cache, and not add the response to the cache, even if the response includes directives in the Cache-Control HTTP header that otherwise indicate that the response is cacheable.

This increases compatibility with NPM packages and JavaScript frameworks that rely on setting the cache property, which is a cross-platform standard part of the Request interface. Previously, if you set the cache property on Request, the Workers runtime threw an exception.

If you've tried to use @planetscale/database, redis-js, stytch-node, supabase, axiom-js or have seen the error message The cache field on RequestInitializerDict is not implemented in fetch — you should try again, making sure that the Compatibility Date of your Worker is set to on or after 2024-11-11, or the cache_option_enabled compatibility flag is enabled for your Worker.