The SCIM population viewer lets you inspect the users and groups provisioned to Cloudflare One by each identity provider. You can check which resources Cloudflare has received without making API requests or opening a support ticket.
In Zero Trust > Integrations > Identity providers, select a user or group count in SCIM resources to open the viewer. You can:
Search users by username, email, name, or resource ID, and groups by name or resource ID.
Inspect resource attributes in formatted or JSON views.
View group members and compare Cloudflare and IdP resource IDs with provisioning logs.
Delete individual resources from the Cloudflare SCIM population when you have edit permission.
The viewer shows provisioned resources rather than provisioning event history. Use SCIM provisioning logs to investigate failed requests. Existing SCIM deprovisioning and policy behavior remain unchanged.
Audit Logs v2 now includes analytics for account-level audit logs in the Cloudflare dashboard. The Actions over time chart separates successful and failed actions for the selected time range.
Summary cards show Total actions, Unique actors, Products impacted, and Failure rate. Select a card to investigate action types, authentication methods, top actors, top products, or failed actions. You can apply or exclude values as filters to update both the analytics and audit log table.
Account analytics are not available for organization-level audit logs.
Browser Run now runs Chrome 152 by default. Your sessions get current web platform features, rendering fixes, and security patches. You do not need to change your code.
This release also includes the following improvements:
Updated default User-Agent. The default User-Agent for Quick Actions now reports Chrome 152 instead of Chrome 119. If you allowlist or match the Browser Run User-Agent, update your rules. You can still set your own value with the userAgent parameter. Refer to Automatic request headers.
Better site compatibility. Quick Actions requests now look more like requests from a regular Chrome browser, so fewer sites block them.
Large response headers. Pages that return large response headers now load correctly.
Updated root certificates. Browser Run removed distrusted root certificates from its trust store.
Absolute links./links now returns absolute URLs, and links-only crawls now follow relative links.
Better Markdown output./markdown and crawl Markdown output now keep code block languages.
Clearer errors. Quick Actions that time out now return a consistent error. Outbound Workers that throw an exception now return an HTTP error to the browser.
Beginning Wednesday, November 4, 2026, D1 will enforce a maximum query result size of 50 MB. Queries that return a result larger than 50 MB will fail and return the following error:
D1 query result exceeded the maximum response size limit of 50 MB
The 50 MB limit covers the 99.999th percentile of requests across D1, so most D1 users will not be impacted.
Import and export operations via the REST API are not subject to this limit and will continue to work as they do today.
If your queries return large results, reduce the result size before the enforcement date. For example, select only the columns you need instead of using SELECT *, and paginate large result sets with LIMIT and OFFSET.
Cloudflare WAN traffic can now use a Gateway processing region with Regional Services. HTTPS traffic enters a Cloudflare data center (that may be outside the selected region) via an IPsec or GRE tunnel, or through Direct CNI, and remains encrypted until Gateway forwards it to the selected region for TLS decryption and HTTP inspection. This feature gives organizations control over where their sensitive traffic is processed, supporting their data sovereignty goals.
This feature requires unified routing. The processing region applies to Gateway HTTP traffic and does not apply to Gateway DNS or network policy processing.
Contact your account team to configure a processing region. For supported regions, configuration details, and limitations, refer to Data residency for Cloudflare One.
The new DNS records experience announced in May is now the default for all users on all plans, including Enterprise. The classic DNS records page has been retired and is no longer available in the Cloudflare dashboard.
Changing a record's type
When the new experience first launched, it did not support changing the type of an existing DNS record. Based on your feedback, we have since added this, so you can now change a record's type (for example, from A to CNAME) directly from the DNS records page. Under the hood, the dashboard uses the batch DNS records endpoint for this.
Questions or feedback
If you run into an issue with the new experience, contact your account team or Cloudflare Support.
Durable Objects can stay active across requests, allowing you to run long-running agents, maintain long-lived WebSocket connections and coordinate state across many clients. This is possible because each Durable Object is globally unique, and this means it must run only one version of your code at a time. Previously, when you deployed a change to your Durable Objects code, that change would be applied immediately, which could interrupt in-flight work and terminate open connections.
Durable Objects now defer code updates by default. When you deploy, an active Durable Object can keep running its current code for up to 5 minutes (300 seconds) while it waits to hibernate. This gives in-flight work time to finish. WebSocket connections that use the Hibernation API remain connected when an object adopts new code after hibernating.
For an active Durable Object, a code update follows this flow:
flowchart TD
accTitle: Code updates for an active Durable Object
accDescr: By default, an active object can keep running its current code until it hibernates or the delay ends. After hibernating, it starts on new code on the next request. If the delay ends first, or immediate mode is selected, it resets and adopts the new code. Deferral is best effort.
A["You deploy new code"] -->|"Default: deferred"| B["The object can keep running<br/>its current code"]
B --> C{"Hibernates before<br/>the delay ends?"}
C -->|Yes| D["Starts on the new code<br/>on the next request"]
C -->|No| E["The object resets<br/>and adopts the new code"]
A -.->|"Override: immediate"| E
To change the delay, add code_update_strategy to durable_objects in your Wrangler configuration:
max_delay is in seconds, from 0 to 86400 (24 hours), with millisecond precision. Omit it to use the default of 300 seconds.
To set the strategy for a single deployment, pass --durable-objects-code-update-mode with immediate or deferred <duration>. For example, apply an update right away during an incident, or when your deployment tooling calls a Durable Object right after deploying and needs the new code:
A deferred code update is best effort and does not guarantee that every operation finishes. The object can still reset before max_delay is reached, for example during a Cloudflare runtime update.
For more information, including how this interacts with gradual deployments and the REST API, refer to Durable Objects code updates.
Keyless SSL supports TLS 1.3 for ECDSA certificates. When TLS 1.3 is enabled on your zone, visitors connecting to a hostname that serves a Keyless ECDSA certificate negotiate TLS 1.3, and your private keys stay on your own key server.
Keyless SSL connections that use RSA certificates continue to negotiate up to TLS 1.2.
This emergency release adds a new rule to the Cloudflare Managed Ruleset, disabled by default, for a vulnerability in Nuxt Image and IPX. The rule offers partial coverage. Please follow the advisory and patch to the latest version.
This release introduces updates to the underlying rule expressions for PHP out-of-memory (OOM) vulnerabilities CVE-2026-109618, CVE-2026-109619 and CVE-2026-115560. The improvements are iterative changes based on reports from Security Researchers as part of PHP Capture the Flag (CTF) challenge ↗︎.
Cloudflare now preserves Workers context when a subrequest targets a Cloudflare-proxied hostname using Bring Your Own IP (BYOIP). These requests are identified as Cloudflare-to-Cloudflare traffic instead of external Internet traffic.
Previously, some BYOIP subrequests could lose Workers context and original client signals. This could cause security rules to behave unexpectedly, Bot Management to evaluate an internal Cloudflare connection instead of the original client, or affected requests to return false 403 responses.
Workers subrequests to BYOIP destinations now preserve the CF-Worker header and cf.worker.upstream_zone field. This supports consistent security rule evaluation across same-zone and cross-zone subrequests.
Bot Management fields on the destination zone now represent the original end-user connection for affected BYOIP subrequests. Bot scores, verified bot status, and JA3/JA4 fingerprints no longer represent the internal connection between Cloudflare services.
Because Cloudflare-proxied BYOIP destinations now follow standard Workers subrequest handling, cross-zone requests set CF-Connecting-IP to the Workers client IP address 2a06:98c0:3600::103 for security reasons. This applies when a Worker in one Cloudflare zone sends a subrequest to a hostname in another Cloudflare zone. Same-zone subrequests continue to reflect the client IP through CF-Connecting-IP.
Agent Skills in the Cloudflare Agents SDK are now files. Write a folder with a SKILL.md into your agent's Computer ↗︎ Workspace, under /workspace/.agents/skills/, and the agent can use it on its next turn. You do not need a build step or a deploy, and the agent can write its own skills.
Three sources. Load skills from a Computer Workspace, an R2 bucket, or code. Think lists every source again before each turn.
The open format. Every SKILL.md is checked against the Agent Skills specification ↗︎. As the specification's guide for agents recommends, a skill that breaks a rule still loads, with a logged warning. Use parseSkill() to check a skill before you write it.
Scripts in the Workspace. Skill scripts are Workspace files, and the model runs them with the Workspace exec tool. Refer to Execution.
Skill tools for any harness. Use the same activate_skill and read_skill_resource tools in Think, Pi Durable, or the AI SDK. Refer to Skills tools.
Warm prompt caches. When a skill changes mid-conversation, Think sends the new catalog as a reminder instead of rebuilding the cached system prompt. An overridden getSystemPrompt() now stays at the top of the system prompt, above context blocks.
This is a breaking change for code that uses agents/skills directly. The agents:skills Vite import, agents/skills/compile, SkillRegistry, and Think's skillWorkspace are removed, and runner() moved to agents/skills/runner. Install Agents SDK v0.32.0 ↗︎ and Think v0.23.0 ↗︎ or later. Refer to the computer-skills ↗︎ and r2-skills ↗︎ examples.
The Cloudflare dashboard now includes a Data Localization Suite Overview, a read-only view of the data localization settings on your account. It shows your Regional Services, Customer Metadata Boundary, and Geo Key Manager settings in one place, so you no longer need to check several dashboard pages and APIs to see where your traffic is processed and where your logs and private keys are stored.
To find it, go to Application Security > Data Localization Suite, or search for "DLS" in the dashboard.
The Overview has a tab for each product:
Regional Services: regionalized hostnames (filter by region or zone), regionalized IP bindings, and regionalized Spectrum applications, each with its region.
Customer Metadata Boundary: whether the boundary is on, the region (US or EU), and whether out-of-region access is allowed.
Geo Key Manager: every custom certificate with a geo policy, with its hostname, zone, region, and status.
Each item links to the dashboard page where you change its setting, and each tab links to the product's documentation.
System for Cross-domain Identity Management (SCIM) is an open standard that synchronizes user identities between an identity provider and a service. For more information, refer to SCIM provisioning. Google Workspace is rolling out custom SCIM profiles in beta. Organizations with access to the feature can use Google's built-in provisioning service to send user updates and deprovisioning events to Cloudflare One.
You must use a generic SAML identity provider in Cloudflare One. The dedicated Google Workspace integration does not support SCIM. Configure a SAML app in Google, then enter the Cloudflare SCIM endpoint and secret in the app's autoprovisioning settings. Turn on Enable user deprovisioning in Cloudflare One to revoke active Access sessions when Google deprovisions a user. Google's configured deprovisioning delay still applies. Support for the existing Google Workspace OIDC client will be added once Google adds support for OIDC + SCIM.
Group membership is configured separately. Have the Google SAML app send a groups attribute to include the user's selected Google groups during authentication. Selecting groups as a SCIM provisioning scope does not synchronize group membership, Google is still adding support for this in their SCIM provider.
Cloudflare One users must first authenticate to Access or register a device with the Cloudflare One Client before SCIM updates can apply. Availability of custom SCIM profiles depends on Google's beta rollout.
MCP tool-call analytics support a response_status query parameter on the account, portal, and server timeseries endpoints. Use success, error, or cached to count one status, or all to include every status.
The default is success. Previously, counts included failed and cached calls. If you need the previous all-status counts, set response_status=all explicitly:
GET /accounts/{account_id}/access/ai-controls/mcp/analytics/tool-calls/timeseries?response_status=all
The response includes the selected response_status. Invalid values, including an empty value, return 400.
Agents that build websites need to check their work in a real browser. Internal hostnames let your agent's browser preview a dev server running in a container without exposing that server to the public Internet.
With internal hostnames, the persistent browser in the Cloudflare Agents SDK can open sites that only your agent serves. Give the browser hostnames such as preview.internal, and its requests to them go to your agent instead of the Internet. The model can check a dev server in its sandbox, a folder in its workspace, or another Worker in the same browser it uses for the web, and nothing is exposed publicly.
Each internal host has a fetch function that runs in your Durable Object, so it can use the object's bindings and state. A host can also send its requests straight to another Worker through a service binding. Add a target schema to a host, and the model chooses what it serves, such as which port or which folder.
Internal hostnames are a capability of the browser, independent of your agent harness. Use the built-in adapters for Pi Durable, AI SDK, and TanStack AI, or connect another harness by implementing a tool with browser.session(). No prebuilt adapter is required.
This example adds a private dev-server preview to a Pi Durable agent. Start with that guide's packages, bindings, and request handler, and install Agents SDK v0.31.0 ↗︎ or later. The example has three parts:
Browser configuration:internalHosts defines how the browser reaches the dev server.
Durable Object wiring: Lifecycle and fetchInternalHost() connect the browser to your application.
Harness integration:browserTool from agents/browser/pi registers the tool with Pi.
import { DurableObject } from "cloudflare:workers";import { createModels } from "@earendil-works/pi-ai/models";import { createRegistry, Harness } from "@earendil-works/pi-durable";import { Browser, browserRun } from "agents/browser";import { browserTool } from "agents/browser/pi";import { PiHarness } from "agents/harness/pi";import { Lifecycle } from "agents/lifecycle";import { createAI } from "agents/models/pi-ai";import { z } from "zod";// Your own code: reach a port on your sandbox and return its Response.import { fetchSandboxPort } from "./hosts";// Browser Run calls back into your Worker through this entrypoint.export { InternalHostRouter } from "agents/browser";export class BrowserAssistant extends DurableObject { ai = createAI({ binding: this.env.AI }); registry = createRegistry(); browser = new Browser({ provider: browserRun(this.env.BROWSER, { internalHosts: { router: this.ctx.exports.InternalHostRouter, hosts: { "preview.internal": { description: "Your dev servers", // What the model passes to browser.assignHost() to choose // what this host serves. fetch receives it as its second argument. target: z.object({ port: z.number().int().min(1024) }), fetch: (request, { port }) => fetchSandboxPort(port, request), }, }, }, }), }); harness = new PiHarness({ harness: ({ storage, context, onWake }) => { const models = createModels(); models.setProvider(this.ai.provider); this.registry.install({ name: "browser", tools: [ browserTool({ browser: this.browser, loader: this.env.LOADER }), ], }); return Harness.open( storage, { models, registry: this.registry, onWake }, context, ); }, defaults: { model: this.ai("@cf/zai-org/glm-5.3-flash") }, }); lifecycle = Lifecycle.install(this).use(this.browser).use(this.harness); // InternalHostRouter calls this method to reach your Browser. fetchInternalHost(message) { return this.browser.fetchInternalHost(message); } async ask(prompt) { const { text } = await this.harness.prompt(prompt); return text; }}
import { DurableObject } from "cloudflare:workers";import { createModels } from "@earendil-works/pi-ai/models";import { createRegistry, Harness } from "@earendil-works/pi-durable";import { Browser, browserRun, type InternalHostMessage } from "agents/browser";import { browserTool } from "agents/browser/pi";import { PiHarness } from "agents/harness/pi";import { Lifecycle } from "agents/lifecycle";import { createAI } from "agents/models/pi-ai";import { z } from "zod";// Your own code: reach a port on your sandbox and return its Response.import { fetchSandboxPort } from "./hosts";// Browser Run calls back into your Worker through this entrypoint.export { InternalHostRouter } from "agents/browser";export class BrowserAssistant extends DurableObject<Env> { ai = createAI({ binding: this.env.AI }); registry = createRegistry(); browser = new Browser({ provider: browserRun(this.env.BROWSER, { internalHosts: { router: this.ctx.exports.InternalHostRouter, hosts: { "preview.internal": { description: "Your dev servers", // What the model passes to browser.assignHost() to choose // what this host serves. fetch receives it as its second argument. target: z.object({ port: z.number().int().min(1024) }), fetch: (request, { port }) => fetchSandboxPort(port, request), }, }, }, }), }); harness = new PiHarness({ harness: ({ storage, context, onWake }) => { const models = createModels(); models.setProvider(this.ai.provider); this.registry.install({ name: "browser", tools: [ browserTool({ browser: this.browser, loader: this.env.LOADER }), ], }); return Harness.open( storage, { models, registry: this.registry, onWake }, context, ); }, defaults: { model: this.ai("@cf/zai-org/glm-5.3-flash") }, }); lifecycle = Lifecycle.install(this).use(this.browser).use(this.harness); // InternalHostRouter calls this method to reach your Browser. fetchInternalHost(message: InternalHostMessage) { return this.browser.fetchInternalHost(message); } async ask(prompt: string) { const { text } = await this.harness.prompt(prompt); return text; }}
To use another harness, keep the browser configuration. Choose a browser-tool adapter if one is available, or implement a tool using browser.session().
browserTool tells the model about each host, including the schema of its target. Because preview.internal has a target schema, the model can choose which port it serves. To check its dev server, the model calls browser.assignHost() with a target that matches the schema, such as { port: 5173 }, and opens the URL it gets back:
The target is checked against the schema and stored, so it lasts across turns, browser restarts, and redeploys. Each request to http://preview.internal/ then calls the host's fetch function with { port: 5173 }.
This release also simplifies browserTool. It runs the model's code in its own Dynamic Worker and no longer needs a CodemodeRuntime export. Refer to the v0.31.0 upgrade notes ↗︎ if you already use browserTool.
For the complete example, limits, and security guidance, refer to Internal hostnames.
Challenge Pages served through the Cloudflare China Network now use Turnstile widgets hosted in Mainland China. This means that visitors in Mainland China can complete challenges faster and more reliably because widgets no longer need to make cross-border requests.
You do not need to take any action: this change applies automatically when a China Network zone presents a Challenge Page. Visitors served by Cloudflare's global network will continue to receive the global Challenge Page experience.
This release introduces updates to the underlying rule expressions for PHP out-of-memory (OOM) vulnerabilities CVE-2026-109618, CVE-2026-109619 and CVE-2026-115560. The improvements are iterative changes based on reports from Security Researchers as part of PHP Capture the Flag (CTF) challenge ↗︎.
VPC Network bindings can select a specific virtual network by setting network_id to cf1:vnet:<UUID>, where the UUID is the ID of the virtual network. Continue to use cf1:network to select your account's default virtual network.
Multiple bindings can select different virtual networks. This lets one Worker reach separate backends that use the same private IP address:
Workers Static Assets can now serve assets from a URL path prefix without changing their directory structure. Set assets.base_path in Wrangler, or worker.assets.basePath in cloudflare.config.ts.
import { defineConfig } from "cf/config";export default defineConfig({worker: { name: "blog", // Set this to today's date compatibilityDate: "2026-10-31", assets: { basePath: "/blog", },},});
With Wrangler, assets.directory selects the directory to serve. With cloudflare.config.ts, your build configuration supplies the assets.
Workers Builds now retains each build and its build logs for 90 days after the build finishes. You can find them under View build history in your Worker's Deployments tab, or through the Workers Builds API.
After 90 days, a build and its logs are removed. The versions and deployments it created stay in your Worker's version history, so you can still roll back to them.
If you need a build's logs for longer, you can download them with the Get build logs API endpoint.
Cloudflare Access now supports HTML and Liquid templates for login, identity-denied, forbidden, and command-line approval pages. Custom templates allow you to control the branding and appearance of each supported page. Liquid variables allow you to provide custom instructions to users or provide a clear reason why they were blocked.
Create templates in the Cloudflare One dashboard, validate them in the editor, and assign them to applications or set account defaults.
For supported page types, template examples, and setup instructions, refer to Access custom pages.
The SQL API introspection endpoint has a new include_attributes query parameter. Setting it to true discovers the keys of a dataset's attributes JSON column, and their types, from the preceding seven days of your account's data. Discovered keys are returned in the dataset's attributes response field.
Some datasets store additional key-value data in an attributes JSON column, which you query with exact-key lookups such as attributes['http.method']. The previous way to discover these keys, include_custom_attributes, returned names in a custom_attributes field. The new names make it clearer that each returned name is a key you access through the attributes column.
include_custom_attributes and custom_attributes are deprecated but still work. Update integrations to use include_attributes and attributes.
You can now restrict a Workflow to a jurisdiction. Its instances only run and store data within that jurisdiction, which helps you comply with data localization regulations such as GDPR ↗︎ or FedRAMP ↗︎.
Set jurisdiction on a workflows binding, or on a Workflow declared in exports:
The supported jurisdictions are eu (the European Union), us (the United States), and fedramp (FedRAMP-Moderate data centers).
A Workflow's jurisdiction is set when the Workflow is created, and cannot be changed afterwards. If an existing Workflow has a different jurisdiction, wrangler deploy and wrangler versions upload stop before uploading your Worker.
Workflow jurisdictions require Wrangler 4.152.0 or above.
@cloudflare/computer ↗︎ gives your agent a workspace to run code in. From v0.7.0, that code can resume after a deploy or crash, and pause on an external action, for example to approve a tool call.
Resume interrupted runs
Set onInterrupt: "replay" on WorkerJavaScriptBackend, or on a single run.
import { DurableObject } from "cloudflare:workers";import { Workspace } from "@cloudflare/computer";import { WorkerJavaScriptBackend } from "@cloudflare/computer/backends/worker-javascript";export class MyAgent extends DurableObject { workspace = new Workspace({ storage: this.ctx.storage, backends: [ new WorkerJavaScriptBackend({ loader: this.env.LOADER, onInterrupt: "replay", }), ], });}
import { DurableObject } from "cloudflare:workers";import { Workspace } from "@cloudflare/computer";import { WorkerJavaScriptBackend } from "@cloudflare/computer/backends/worker-javascript";export class MyAgent extends DurableObject<Env> { workspace = new Workspace({ storage: this.ctx.storage, backends: [ new WorkerJavaScriptBackend({ loader: this.env.LOADER, onInterrupt: "replay", }), ], });}
When recovery is triggered, the JavaScript runs again from the beginning, reusing recorded results for completed calls without printing previously recorded output again. If a call's result was not recorded before the interruption, recovery may repeat the call — even if its action already succeeded. For retry handling, including unsafeToRepeat() for operations that cannot safely repeat, refer to Calls that must not run twice.
Recovery starts on the next call to workspace.runtime. To recover without waiting for traffic, call workspace.runtime.recover() from a Durable Object alarm.
Wait for approvals
Expose your application functions through custom modules. A function can call ctx.interrupt() to pause the run until your application supplies an approval decision.
Require approval before posting a comment
For example, to provide a GitHub module that allows an agent to post a comment on an issue with approval, an implementation of postComment could look like this:
new WorkerJavaScriptBackend({ loader: this.env.LOADER, onInterrupt: "replay", modules: { // The agent's code imports this with `import { postComment } from "ws:github"`. "ws:github": { postComment: async ([issue, body], ctx) => { const { approved } = await ctx.interrupt({ issue, body, }); if (!approved) throw new Error("The comment was rejected."); return github.comment(issue, body, { idempotencyKey: ctx.callId }); }, }, },});
new WorkerJavaScriptBackend({ loader: this.env.LOADER, onInterrupt: "replay", modules: { // The agent's code imports this with `import { postComment } from "ws:github"`. "ws:github": { postComment: async ([issue, body], ctx) => { const { approved } = await ctx.interrupt<{ approved: boolean }>({ issue, body, }); if (!approved) throw new Error("The comment was rejected."); return github.comment(issue, body, { idempotencyKey: ctx.callId }); }, }, },});
Call it from agent code
When the agent's code calls postComment(), the run pauses at ctx.interrupt() until your app responds. The agent's code calls it like any other function:
import { postComment } from "ws:github";export default async function ({ issue }) { return postComment(issue, "Hello world");}
Respond to the approval request
The time spent waiting does not count toward its timeout. The wait also survives deploys. Fetch the pending calls with workspace.runtime.pending() and resolve them with respond():
for (const call of await workspace.runtime.pending()) { await workspace.runtime.respond(call.id, call.callId, { approved: true });}
for (const call of await workspace.runtime.pending()) { await workspace.runtime.respond(call.id, call.callId, { approved: true });}
The run continues once all pending calls are resolved.