Skip to content

Changelog

New updates and improvements at Cloudflare.

Pending I/O operations allow Durable Objects to continue long-running work without a connected client

Durable Objects remain active while handling a request from a connected client. This change applies when no client is connected, such as when an agent continues a submitted job after its client disconnects.

This behavior is the default for Workers with a compatibility date of 2026-10-01 or later. To use it with an earlier date, add the durable_object_io_tasks_prevent_eviction compatibility flag. To opt out, add the durable_object_io_tasks_do_not_prevent_eviction flag.

Pending service binding requests now keep Durable Objects running while they wait for a response. Pending calls to another Durable Object through remote procedure call (RPC) or fetch(), as well as this.ctx.container.monitor(), now also keep the Durable Object running.

Promises passed to this.ctx.waitUntil() and pending setTimeout() and setInterval() timers also receive this protection.

Previously, Cloudflare could shut down an idle Durable Object while one of these operations remained pending without a connected client. This could stop unfinished work.

This change helps you run long-running tasks such as agents. An agent can call tools through service bindings, coordinate with other Durable Objects, or wait for a container process without relying on the original client to remain connected.

Outbound fetch() requests to external services, TCP sockets, and outbound WebSockets already keep Durable Objects running.

Each pending operation prevents idle shutdown for up to 15 minutes. Starting another one later can extend the Durable Object's time in memory. The limit applies to each operation, not to the total time in memory.

Timeline of a service binding fetch, an RPC call, and monitor() each preventing eviction for up to 15 minutes

Duration charges continue while an operation prevents eviction.

For more information, refer to Lifecycle of a Durable Object.

Web Crypto adds ML-KEM and ML-DSA support

The Workers Web Crypto API now supports ML-KEM-768, ML-KEM-1024, ML-DSA-44, ML-DSA-65, and ML-DSA-87. ML-KEM establishes shared secrets, while ML-DSA signs and verifies data.

The opt-in API also adds key encapsulation and decapsulation methods, getPublicKey(), SubtleCrypto.supports(), and JSON Web Keys (JWKs) with the AKP key type.

Turn on the webcrypto_modern_algorithms compatibility flag to use these features:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "compatibility_flags": [
    "webcrypto_modern_algorithms"
  ]
}
compatibility_flags = ["webcrypto_modern_algorithms"]

This example uses ML-KEM-768 to establish the same shared secret on both sides:

src/index.jsjs
const keyPair = await crypto.subtle.generateKey("ML-KEM-768", false, [
	"encapsulateBits",
	"decapsulateBits",
]);

if (!("publicKey" in keyPair)) {
	throw new Error("Expected an ML-KEM key pair");
}

const { sharedKey, ciphertext } = await crypto.subtle.encapsulateBits(
	"ML-KEM-768",
	keyPair.publicKey,
);

const recoveredSharedKey = await crypto.subtle.decapsulateBits(
	"ML-KEM-768",
	keyPair.privateKey,
	ciphertext,
);
src/index.tsts
const keyPair = await crypto.subtle.generateKey("ML-KEM-768", false, [
	"encapsulateBits",
	"decapsulateBits",
]);

if (!("publicKey" in keyPair)) {
	throw new Error("Expected an ML-KEM key pair");
}

const { sharedKey, ciphertext } = await crypto.subtle.encapsulateBits(
	"ML-KEM-768",
	keyPair.publicKey,
);

const recoveredSharedKey = await crypto.subtle.decapsulateBits(
	"ML-KEM-768",
	keyPair.privateKey,
	ciphertext,
);

Workers implements a subset of the evolving Modern Algorithms in the Web Cryptography API ↗︎ draft. ML-KEM-512 and the draft's other algorithms are not supported. The API may change as the draft evolves.

For current algorithm and operation support, refer to Web Crypto supported algorithms.

New scheduling policy for Containers to configure image and instance from Durable Objects

Containers now support the durable_object scheduling policy in public beta. This policy lets a Durable Object select the image and instance size for a Container at runtime instead of using one centrally managed configuration for the application.

To use custom images, configure the policy and one or more named images in Wrangler:

{
	"containers": [
		{
			"class_name": "AgentComputer",
			"scheduling_policy": "durable_object",
			"images": {
				"base": {
					"dockerfile": "./container/Dockerfile",
				},
			},
		},
	],
}
[[containers]]
class_name = "AgentComputer"
scheduling_policy = "durable_object"

[containers.images.base]
dockerfile = "./container/Dockerfile"

Wrangler prepares each image and exposes its immutable reference through ctx.container.images. Supply that reference and an instance size when you start the Container:

src/index.jsjs
this.ctx.container.start({
	image: this.ctx.container.images.base,
	enableInternet: false,
	instance: "standard-2",
});
src/index.tsts
this.ctx.container.start({
	image: this.ctx.container.images.base,
	enableInternet: false,
	instance: "standard-2",
});

The durable_object policy also supports the new cloudflare/debian-trixie Cloudflare-managed image, which includes Node.js 24.20.0 on Debian Trixie slim. Start it directly without configuring a named image.

Durable Object-managed Container instances have independent lifecycles and do not participate in application-wide image rollouts.

For configuration, runtime sizing, snapshots, and update behavior, refer to Scheduling Policies.

Snapshot and restore Container filesystem

Containers now support snapshot APIs in public beta for saving and restoring point-in-time filesystem state. Create a snapshot first, then pass it back to start() to restore files after container sleep, restart, or handoff to another Durable Object.

Use snapshotContainer() through the Durable Object Container API to capture the full container filesystem. Create a snapshot from a running Container, store its handle, and pass that handle to start() when you restore it later:

src/index.jsjs
import { DurableObject } from "cloudflare:workers";

export class MyDurableObject extends DurableObject {
	async saveSnapshot() {
		// Create a snapshot from the running Container.
		const containerSnapshot = await this.ctx.container.snapshotContainer({});

		await this.ctx.storage.put("containerSnapshot", containerSnapshot);
	}

	async restoreSnapshot() {
		// Restore the saved snapshot later.
		const containerSnapshot = await this.ctx.storage.get("containerSnapshot");

		if (!containerSnapshot) {
			return;
		}

		this.ctx.container.start({ containerSnapshot, enableInternet: false });
	}
}
src/index.tsts
import { DurableObject } from "cloudflare:workers";

export class MyDurableObject extends DurableObject {
	async saveSnapshot() {
		// Create a snapshot from the running Container.
		const containerSnapshot = await this.ctx.container.snapshotContainer({});

		await this.ctx.storage.put("containerSnapshot", containerSnapshot);
	}

	async restoreSnapshot() {
		// Restore the saved snapshot later.
		const containerSnapshot =
			await this.ctx.storage.get<ContainerSnapshot>("containerSnapshot");

		if (!containerSnapshot) {
			return;
		}

		this.ctx.container.start({ containerSnapshot, enableInternet: false });
	}
}

Snapshots are only supported for Container applications that use the durable_object scheduling policy. Snapshots are immutable, so create a new snapshot to persist filesystem changes made after a restore.

For more information, refer to Snapshots and the Durable Object Container API.

Sandbox SDK 1.0: control every sandbox from your own Durable Object

Sandbox SDK 1.0 is available. Your own Durable Object class now controls each sandbox container directly, through the Durable Object container API on this.ctx.container.

With 1.0, your class can:

  • Choose the image and instance size each time it starts a sandbox. One class can run sandboxes on different images, and a deploy does not restart sandboxes that are running.
  • Save the files of a sandbox as a snapshot, in public beta, and start the same sandbox or a new one from it.
  • Decide when each sandbox stops, for example when a task finishes, when its user goes idle, or after it saves a snapshot.
  • Run commands with streamed input and output, send them signals, and open terminals.
  • Serve previews from ports in the sandbox, with your own hostnames and authentication.
  • Handle outbound requests for each hostname in Worker code, so credentials and bindings stay in your Worker.
  • Expose only the methods that you want callers to use.
  • Run an agent in the same Durable Object, and give the model a tool that runs commands in the sandbox.
src/index.jsjs
import { Files } from "@cloudflare/sandbox";
import { DurableObject } from "cloudflare:workers";

export class MySandbox extends DurableObject {
	container;
	files;

	constructor(ctx, env) {
		super(ctx, env);
		if (!ctx.container) {
			throw new Error("No container is configured");
		}
		this.container = ctx.container;
		this.files = new Files(ctx.container);
	}

	async run(script) {
		if (!this.container.running) {
			// Your code chooses the image, size, and network access.
			this.container.start({
				image: this.container.images.sandbox,
				instance: "lite",
				enableInternet: false,
			});
		}

		await this.files.writeFile("/tmp/task.sh", script);
		const proc = await this.container.exec(["sh", "/tmp/task.sh"]);
		return proc.output();
	}
}
src/index.tsts
import { Files } from "@cloudflare/sandbox";
import { DurableObject } from "cloudflare:workers";

export class MySandbox extends DurableObject<Env> {
	private readonly container: Container;
	private readonly files: Files;

	constructor(ctx: DurableObjectState, env: Env) {
		super(ctx, env);
		if (!ctx.container) {
			throw new Error("No container is configured");
		}
		this.container = ctx.container;
		this.files = new Files(ctx.container);
	}

	async run(script: string) {
		if (!this.container.running) {
			// Your code chooses the image, size, and network access.
			this.container.start({
				image: this.container.images.sandbox,
				instance: "lite",
				enableInternet: false,
			});
		}

		await this.files.writeFile("/tmp/task.sh", script);
		const proc = await this.container.exec(["sh", "/tmp/task.sh"]);
		return proc.output();
	}
}

Your class uses the container API directly, with the durable_object scheduling policy and container snapshots, both in public beta. @cloudflare/sandbox adds classes for work that the API does not include:

  • Files streams files in and out of the running sandbox.
  • S3Mount mounts an S3-compatible bucket, such as R2. Your Worker signs each storage request, so the credentials stay out of the sandbox.
  • DirectoryBackup saves a directory to R2 and restores it into any sandbox, including one on a newer image.

If you use Sandbox SDK 0.x

Your 0.x applications keep running, and @cloudflare/sandbox 0.x stays on npm. Sandbox SDK 0.x receives bug and security fixes until 2026-12-31, and its documentation stays at Sandbox SDK 0.x.

When you are ready, Migrate from Sandbox SDK 0.x shows the 1.0 code for each 0.x feature, including preview URLs, tunnels, background processes, terminals, backups, and the code interpreter. The guide keeps the preview URLs and named tunnels that your 0.x application created working. You can move every sandbox in one deploy, or run a 1.0 class next to your 0.x class and move sandboxes one at a time. The deploy that moves an existing class to the new policy is one-way, so the guide shows how to rehearse it first.

The Sandboxes documentation also covers Dynamic Workers, for untrusted code in JavaScript, Python, or WebAssembly.

Cloudflare One Client for Windows (version 2026.8.2033.1)

A new Beta release for the Windows Cloudflare One Client is now available on the beta releases downloads page.

This beta release includes the following changes and improvements:

  • Fixed an issue that could briefly block traffic to split tunnel excluded resources while the client was connecting or reconnecting.
  • Improved reauthentication reliability and fixed an issue where a reauthentication could force a new registration.
  • Improved client reaction to the current network lowering its MTU.
  • Added support for routing non-RFC 1918 local IPv4 networks through the WARP tunnel when unrestricted LAN inclusion is enabled by policy or MDM.
  • Improved DNS reliability on networks with lower MTUs by clamping the TCP maximum segment size (MSS) for DNS-over-HTTPS connections sent through the tunnel.
  • Improved API reliability by retrying requests dropped when reusing pooled connections.
  • The client no longer requires the Windows WLAN AutoConfig service to be running.
  • Implemented a service recovery mechanism backed by Windows scheduler task to start WARP service on system unlock if not already started.
  • Fixed slow captive portal checks causing the client service to become unresponsive or restart while connecting.
  • Fixed a race when switching tunnel protocols during key rotation that could prevent WireGuard from connecting.
  • Fixed the client continuing to report 'No network' after a successful manual disconnect.
  • Fixed Digital Experience Monitoring (DEX) HTTP tests failing TLS validation on Windows.
  • Fixed the client UI crashing at startup when it could not write to the Windows registry.
  • Fixed latency spikes and traffic interruptions during TPM-backed API authentication when hardware-backed registration is enabled.
  • Fixed trailing whitespace in BIOS serial numbers causing serial-number and client-certificate device posture checks to fail.
  • Fixed a client UI crash that could occur when the daemon connection was reset during an IPC request.
  • Fixed a startup crash when date formatting data for the system locale had not yet loaded.

Known issues

  • None

For Zero Trust documentation, see: https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/ For Consumer documentation, see: https://developers.cloudflare.com/warp-client/

Pay for AI inference with Machine Payments

AI Gateway now supports Machine Payments in beta. With Machine Payments, clients can use the x402 protocol to pay for eligible inference requests directly from a stablecoin wallet instead of maintaining a prepaid credit balance.

Machine Payments is available for the /ai/run endpoint with select open models. To request x402 payment, authenticate with a Cloudflare API token and include the Cloudflare-specific Payment-Method: x402 header:

curl -iX POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/ai/run" \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Payment-Method: x402" \
  --header "Content-Type: application/json" \
  --data '{
    "model": "z-ai/glm-4.7-flash",
    "input": {
      "messages": [
        {
          "role": "user",
          "content": "What is Cloudflare?"
        }
      ]
    }
  }'

An x402-compatible client handles the payment challenge, signs an authorization from the client's wallet, and retries the request. Machine Payments currently requires customers to be based in the United States and have a credit card on file.

For prerequisites, eligible models, and transaction details, refer to Machine Payments (x402).

Role-based access control for Browser Isolation policies

Isolation policies support role-based access control (RBAC). Because isolation policies are Gateway HTTP policies with the Isolate action, Gateway's account-level and resource-scoped roles apply to them directly.

Use the Zero Trust HTTP Policies Admin account-level role to grant access to all HTTP policies in the account. You can also assign a resource-scoped role to let a team member manage a specific isolation policy without exposing other Gateway resources.

Policy settings such as copy/paste, file download/upload, keyboard, and printing are part of the policy object and follow the same permissions.

For setup instructions, refer to Granular permissions for Gateway.

Monetization Gateway closed beta

Monetization Gateway is now available in closed beta. Sellers can use it to charge agents for access to APIs, Model Context Protocol (MCP) tools, sites, and datasets.

Sellers (domain owners) define which requests require payment, the cost, and where the payment should be sent. Buyers receive the payment instructions, sign an authorization, and receive the resource after the payment has been settled. The Monetization Gateway uses the x402 protocol to handle payment authorization within the HTTP request flow.

To learn more, request access in the Cloudflare dashboard ↗︎, review the Monetization Gateway documentation, or read the blog ↗︎.

WAF Release - 2026-09-30

This release introduces new detections to enhance protection against a specific GitLab path traversal vulnerability, alongside advanced generic rules targeting HTTP request smuggling, directory traversal, and command injection attempts.

Key Findings

  • CVE-2026-85706: A path traversal vulnerability affecting GitLab.
RulesetRule IDLegacy Rule IDDescriptionPrevious ActionNew ActionComments
Cloudflare Managed RulesetN/ABroken Access Control - Directory TraversalLogBlockThis is a new detection.
Cloudflare Managed RulesetN/AHTTP Request Smuggling - Request Body Anomaly - BetaLogBlockThis rule is merged into the original rule "HTTP/2 Request Smuggling - Request Body Anomaly" (ID: ).
Cloudflare Managed RulesetN/ACommand Injection - Generic 8 - body - BetaDisabledDisabledThis rule is merged into the original rule "Command Injection - Generic 8 - body" (ID: ).
Cloudflare Managed RulesetN/AGitLab - Path Traversal- CVE:CVE-2026-85706LogBlockThis is a new detection.
Cloudflare Managed RulesetN/AGeneric - Request routing cache inconsistencyN/ABlockThis is a new detection.

WAF Release - Scheduled changes for 2026-10-06

Announcement DateRelease DateRelease BehaviorLegacy Rule IDRule IDDescriptionComments
2026-09-222026-10-06LogN/ACommand Injection - Generic 8 - uri - Beta

This rule will be merged into the original rule "Command Injection - Generic 8 - uri" (ID: ).

2026-09-302026-10-06LogN/AF5 BIG-IP - UnAuth Heap-Overflow - CVE:CVE-2026-94127

This is a new detection.

Cloudflare One Client for Windows (version 2026.8.2028.1)

A new Beta release for the Windows Cloudflare One Client is now available on the beta releases downloads page.

This beta release includes the following changes and improvements:

  • Fixed an issue that could briefly block traffic to split tunnel excluded resources while the client was connecting or reconnecting.
  • Improved reauthentication reliability and fixed an issue where a reauthentication could force a new registration.
  • Improved client reaction to the current network lowering its MTU.
  • Added support for routing non-RFC 1918 local IPv4 networks through the WARP tunnel when unrestricted LAN inclusion is enabled by policy or MDM.
  • Improved DNS reliability on networks with lower MTUs by clamping the TCP maximum segment size (MSS) for DNS-over-HTTPS connections sent through the tunnel.
  • Improved API reliability by retrying requests dropped when reusing pooled connections.
  • The client no longer requires the Windows WLAN AutoConfig service to be running.
  • Implemented a service recovery mechanism backed by Windows scheduler task to start WARP service on system unlock if not already started.
  • Fixed slow captive portal checks causing the client service to become unresponsive or restart while connecting.
  • Fixed a race when switching tunnel protocols during key rotation that could prevent WireGuard from connecting.
  • Fixed the client continuing to report 'No network' after a successful manual disconnect.
  • Fixed Digital Experience Monitoring (DEX) HTTP tests failing TLS validation on Windows.
  • Fixed the client UI crashing at startup when it could not write to the Windows registry.
  • Fixed latency spikes and traffic interruptions during TPM-backed API authentication when hardware-backed registration is enabled.
  • Fixed trailing whitespace in BIOS serial numbers causing serial-number and client-certificate device posture checks to fail.
  • Fixed a client UI crash that could occur when the daemon connection was reset during an IPC request.
  • Fixed a startup crash when date formatting data for the system locale had not yet loaded.

Known issues

  • None

Cloudflare One Client for macOS (version 2026.8.2028.1)

A new Beta release for the macOS Cloudflare One Client is now available on the beta releases downloads page.

This beta release includes the following changes and improvements:

  • Fixed an issue that could briefly block traffic to split tunnel excluded resources while the client was connecting or reconnecting.
  • Improved reauthentication reliability and fixed an issue where a reauthentication could force a new registration.
  • Improved client reaction to the current network lowering its MTU.
  • Added support for routing non-RFC 1918 local IPv4 networks through the WARP tunnel when unrestricted LAN inclusion is enabled by policy or MDM.
  • Improved DNS reliability on networks with lower MTUs by clamping the TCP maximum segment size (MSS) for DNS-over-HTTPS connections sent through the tunnel.
  • Improved API reliability by retrying requests dropped when reusing pooled connections.
  • Fixed Extra Logging failing to capture packets across all interfaces.
  • Fixed an issue that could prevent remote diagnostics from completing.
  • Fixed DNS connectivity checks failing on IPv6-only networks.
  • Fixed the client service exiting when its route-monitoring socket was closed after sleep or wake.
  • Fixed DNS enforcement checks making the client service unresponsive on systems with large routing tables.
  • Fixed slow captive portal checks causing the client service to become unresponsive or restart while connecting.
  • Fixed a race when switching tunnel protocols during key rotation that could prevent WireGuard from connecting.
  • Fixed the client continuing to report 'No network' after a successful manual disconnect.
  • Fixed a client UI crash that could occur when the daemon connection was reset during an IPC request.
  • Fixed a startup crash when date formatting data for the system locale had not yet loaded.

Known issues

  • None

Identify model overuse and potential savings with User Insights

AI Gateway User Insights now gives you more context about the traffic flowing through your gateway. It shows what users and agents are doing with AI, and where a selected model may be more capable than a task requires.

On the analysis side, User Insights groups conversations by task, tracks conversation turns, and helps you compare model fit with cost and latency.

User Insights task and model analysis grouped by task categories

The Potential Savings view highlights requests that may work with faster or less expensive models without compromising output quality. These are the same signals that Cloudflare's Auto Router uses to select a model based on task and cost.

Potential Savings view comparing tasks and suggested models

These new insights are available to all AI Gateway customers at no additional cost. For more information, refer to User Insights.

Connect multiple clients to one Browser Run session

Browser Run sessions now accept multiple concurrent connections. Before, a session accepted only one connection at a time, and other Workers had to wait until that connection closed. Now multiple Workers can connect to the same browser at the same time.

Each puppeteer.connect() call opens its own Chrome DevTools Protocol (CDP) connection. Create a separate browser context for each request to keep its pages, cookies, and storage apart from other clients.

const browser = await puppeteer.connect(env.MYBROWSER, sessionId);
const context = await browser.createBrowserContext();

try {
	const page = await context.newPage();
	await page.goto("https://example.com");
	// ...
} finally {
	await context.close();
	await browser.disconnect(); // keep the shared browser running
}
const browser = await puppeteer.connect(env.MYBROWSER, sessionId);
const context = await browser.createBrowserContext();

try {
	const page = await context.newPage();
	await page.goto("https://example.com");
	// ...
} finally {
	await context.close();
	await browser.disconnect(); // keep the shared browser running
}

Sharing sessions means fewer new browsers to launch, less cold-start time, and fewer concurrent browsers counted against your limits.

Concurrent connections require @cloudflare/puppeteer version 1.1.0 or later.

Refer to Reuse sessions for a full example.

Custom Container instance types no longer have a disk to memory ratio limit

Containers custom instance types no longer limit disk based on memory. Previously, a custom instance type could have a maximum of 2 GB of disk for each 1 GiB of memory. You can now allocate up to the 20 GB disk maximum to any custom instance type.

Use this to run workloads that need more disk than memory, such as workloads with large container images, datasets, or build caches. The maximum image size is the same as the instance disk space, so more disk also lets you deploy larger images.

For example, a custom instance type with 1 vCPU and 3 GiB of memory was previously limited to 6 GB of disk. It can now use 20 GB:

{
	"containers": [
		{
			"image": "./Dockerfile",
			"instance_type": {
				"vcpu": 1,
				"memory_mib": 3072,
				"disk_mb": 20000,
			},
		},
	],
}
[[containers]]
image = "./Dockerfile"

  [containers.instance_type]
  vcpu = 1
  memory_mib = 3_072
  disk_mb = 20_000

The other custom instance type constraints do not change, including the minimum of 3 GiB of memory per vCPU. For the full list, refer to Custom Instance Types.

Identify Mesh, Workers VPC, and Cloudflare Tunnel replicas in network logs

You can now tell a person on a laptop apart from a Mesh node or an AI agent running on Workers, without matching on connector email addresses or Mesh IP ranges — and see exactly which Cloudflare Tunnel and cloudflared replica received each session.

Gateway network logs and Zero Trust Network Session Logs now identify two new kinds of traffic:

  • Mesh — Traffic sent from or delivered to a Cloudflare Mesh node. Previously, Mesh nodes were logged the same way as devices running the Cloudflare One Client, because Mesh nodes run the client in headless mode.
  • Workers VPC — Traffic sent by a Worker through a Workers VPC binding. Previously, Workers VPC sessions were not recorded in Network Session Logs.
Viewing Mesh and Workers VPC traffic in Gateway network logs

Gateway network logs

To view these values in the dashboard, go to Zero Trust > Insights & Logs > Logs > Network logs, select Columns, and turn on Traffic Source and Traffic Destination. Both values also appear under Network query details when you open a log entry.

Network Session Logs

The zero_trust_network_sessions dataset, available through Logpush, includes the following fields:

Field Description
OnrampType How the session entered Cloudflare One. Values: CF1_CLIENT, MESH, WORKERS_VPC, MAGIC, OTHER.
Offramp Where the session was routed. Sessions routed to a Mesh node report MESH.
SourceName Name of the Worker that started the session. Only populated for Workers VPC sessions.
SourceID Stable identifier of the Worker that started the session. Only populated for Workers VPC sessions.
DestinationReplicaID The replica that served the session, such as a specific replica of a Mesh node or a cloudflared replica of a Cloudflare Tunnel.

For example, OnrampType = 'WORKERS_VPC' AND Offramp = 'MESH' returns every session where a Worker reached a service behind a Mesh node, and SourceName tells you which Worker it was.

See which tunnel and replica received a session

With DestinationReplicaID, you can now confirm which Cloudflare Tunnel and which cloudflared replica received traffic for a specific session. Combine it with the existing DestinationTunnelID field to trace a session to an exact tunnel replica — or Mesh node replica — when you run multiple replicas for high availability. The replica ID matches the Connector ID shown in the dashboard, so you can stream that replica's logs with cloudflared tail --connector-id.

Sessions logged before this change are not backfilled. For all available fields, refer to Zero Trust Network Session Logs.

Browser Run adds WebMCP to Kitesurf and moves to document.modelContext

WebMCP now works in Kitesurf sessions as well as Lab sessions. Both backends use the document.modelContext API from the WebMCP Community Group draft ↗︎. Lab sessions no longer expose navigator.modelContextTesting.

To list and run page tools:

  • Chrome DevTools: Use the Application > WebMCP panel in the live view of a Lab session or in the Kitesurf playground ↗︎.
  • AI agents: Start Chrome DevTools MCP with the --category-experimental-webmcp flag to add the list_webmcp_tools and execute_webmcp_tool tools.
  • CDP clients: Use the WebMCP CDP domain.

Invalidate cached content instead of purging it

You can now invalidate cached content instead of purging it. Invalidation marks matching content as stale. On the next request, Cloudflare revalidates the content with your origin. If your origin responds with 304 Not Modified, Cloudflare reuses the cached content instead of downloading it again.

Use invalidation to refresh a group of assets when only some of them have changed. For example, invalidate all content that shares a cache tag. Cloudflare reuses unchanged assets instead of downloading them again. This requires your origin to return an ETag or Last-Modified header and support conditional requests.

Invalidation supports the same selectors as purge: URLs, cache tags, hostnames, URL prefixes, and everything. To invalidate content, send a POST request to the new invalidate_cache endpoint:

curl --request POST \
  "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/invalidate_cache" \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{"tags":["product-images"]}'

In the dashboard, use Invalidate Cache on the Caching > Configuration page.

Your cache settings determine whether Cloudflare serves stale content while it revalidates. Cloudflare can also serve invalidated content stale if your origin returns a 5xx error or cannot be reached. To stop serving cached content, purge it instead.

Invalidation requests count toward the same rate limits as purge requests.

Purge behavior for Cache Reserve also changes with this release. For details, refer to Cache Reserve purge behavior.

For more information, refer to Invalidate cached content.

Purge now forces a cache miss for Cache Reserve content

Purge requests now force a cache miss for Cache Reserve content, regardless of purge type. Previously, purging by cache tag, hostname, prefix, or everything marked matching Cache Reserve content for revalidation. Purging by URL already removed content from Cache Reserve and is unchanged.

This change applies to purge requests from the API and the dashboard. Cache Reserve now handles purges the same way as the edge cache.

Cost impact

After a purge, the next request for affected content is a Cache Reserve miss. Your origin must deliver the content in full, even if it has not changed. Cloudflare then writes the content to Cache Reserve again, which is billed as a Class A operation.

Purging by tag, hostname, prefix, or everything does not delete content from Cache Reserve right away. Matching content continues to incur storage costs until a later request replaces it or its retention period ends.

If you frequently purge Cache Reserve content by tag, hostname, prefix, or everything, review the effect on your origin egress and Cache Reserve usage.

Keep revalidating Cache Reserve content

To keep content in Cache Reserve and revalidate it instead, send the same request to the new invalidate_cache endpoint:

curl --request POST \
  "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/invalidate_cache" \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{"tags":["product-images"]}'

In the dashboard, use Invalidate Cache on the Caching > Configuration page.

If your origin responds with 304 Not Modified, Cloudflare reuses the stored content instead of fetching it from your origin again. Compared with purging, invalidation reduces origin egress but not Cache Reserve operations. Updating the stored content after a 304 response is still a Class A operation. Invalidating by URL also updates the stored content when you send the request, which is a Class A operation.

Unlike the previous purge behavior, invalidation can serve stale content while it revalidates if your cache settings allow it. This applies only to copies in the edge cache, not to content served from Cache Reserve. Cloudflare can also serve invalidated content stale if your origin returns a 5xx error or cannot be reached. For details, refer to Invalidate cached content.

Cloudflare CLI is now in beta

The Cloudflare CLI, cf, is now in beta. cf is one command-line interface for the public Cloudflare API and for Workers projects. Use it to manage zones, DNS, storage, and security settings, and to create, develop, and deploy Workers, without switching between tools.

Install cf globally, then sign in:

npm install --global cf
cf auth login

With cf, you can:

  • Manage resources across Cloudflare. More than 2,900 commands cover the public Cloudflare API, and most print their results as JSON.
  • Create and deploy Workers. cf init creates a project that uses cloudflare.config.ts, a typed configuration file. cf dev, cf build, and cf deploy develop, build, and deploy it.
  • Move from Wrangler. cf migrate converts a Wrangler configuration file to cloudflare.config.ts. You can also run cf resource commands in an existing Wrangler project without migrating it.
  • Work with coding agents. cf cli search finds the command for a task from a plain-language description, so an agent can find and run commands without prior knowledge of cf.

cf is in beta. Commands, configuration, and Build Output can change before the stable release.

To get started, refer to Install and sign in. To move an existing project, refer to Migrate a Wrangler project.

Call Workflows declared in `exports` through `ctx.exports`

A Worker can now call the Workflows it declares in the exports field of its Wrangler configuration through ctx.exports. You no longer need a workflows binding to call a Workflow from the Worker that defines it.

Each Workflow is keyed by class name, and has the same API as a Workflow binding:

src/index.jsjs
export default {
	async fetch(request, env, ctx) {
		const instance = await ctx.exports.MyWorkflow.create({
			params: { name: "World" },
		});
		return Response.json({ id: instance.id });
	},
};
src/index.tsts
export default {
	async fetch(request, env, ctx): Promise<Response> {
		const instance = await ctx.exports.MyWorkflow.create({
			params: { name: "World" },
		});
		return Response.json({ id: instance.id });
	},
} satisfies ExportedHandler<Env>;

A workflows binding and a workflow export with the same name share their instances. You can move a Workflow from a binding to an export without losing its instances.

wrangler dev, the Cloudflare Vite plugin, and the Workers Vitest integration run Workflows on ctx.exports locally. Local development requires Wrangler 4.142.0, @cloudflare/vite-plugin 1.61.0, or @cloudflare/vitest-plugin 1.3.0 or above.

In Vitest, introspectWorkflow() and introspectWorkflowInstance() still need a Workflow binding. To introspect a Workflow declared in exports, add a test-only binding to it.

For more information, refer to Call a Workflow through ctx.exports.

Subscribe to Browser Run crawl events

Browser Run crawl jobs can publish lifecycle events to Cloudflare Queues. Subscribe to started, updated, and finished events to track progress or trigger downstream processing without polling.

To create an account-level subscription, run the following command:

npx wrangler queues subscription create <QUEUE_NAME> --source browserRun --events crawl.started,crawl.updated,crawl.finished

For payload examples, refer to the Browser Run event schemas.

Suppress recipients for one sending domain

Email Sending suppressions now have a scope:

  • account: The suppression applies to every sending domain and subdomain in your account. This is the default.
  • sending_domain: The suppression applies to one sending domain only. A suppression for mail.myappexample.com does not block mail from myappexample.com.

Most importantly, Email Sending now automatically creates bounce and complaint suppressions at the sending-domain level. This provides greater granularity by preventing an issue with one sending domain from suppressing the recipient across your entire account.

To add a suppression for one sending domain in the dashboard, go to Email Sending > Suppressions and select Sending domain in Scope. Imports can also set a scope for each row or a default scope.

In the API, pass scope when you create the suppression:

curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/email/sending/suppressions \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "email": "user@example.net",
    "scope": { "type": "sending_domain", "value": "mail.myappexample.com" }
  }'

The suppressions API returns scope on every suppression. To list the suppressions for one domain, use scope_type=sending_domain&scope_value=mail.myappexample.com. If you omit scope, the API creates an account suppression, so existing integrations continue to work.

Refer to Suppression lists and Manage suppressions for details.