Workers for Platforms: Giving SaaS Customers Code-Level Customization
SaaS has become the default way businesses consume software, but managed applications inevitably hit a wall: every customer has workflows, integrations, or response formats that a one-size-fits-all product can't accommodate. Building custom features per customer is unsustainable, forcing engineering teams away from core product work.
Cloudflare is addressing this with Workers for Platforms, now generally available for all Enterprise customers. The service lets SaaS vendors expose serverless functions to their own end developers, who can deploy custom Workers that run inside the vendor's application. Existing enterprises should contact their Customer Success Manager for access; new customers can reach out through Cloudflare's contact form.
The Architecture: Dispatch Workers and User Workers
Workers for Platforms introduces a separation between the platform's own code and the custom code written by end developers.
- Dispatch Workers are authored by the platform (the SaaS vendor). They run first on any request, handling authentication, boilerplate logic, response sanitization, and—crucially—routing to the appropriate end-developer Worker. Dispatch Workers are configured the same way as standard Workers, but require a Dispatch Namespace binding in
wrangler.toml. - User Workers are deployed by end developers. These can script automations, build integrations, or alter response payloads. End developers simply upload code; the platform and Cloudflare handle infrastructure, so there's no third-party FaaS setup to maintain.
The routing example below shows a Dispatch Worker reading a subdomain from the request path and calling the matching User Worker. Alternatively, a platform can map request attributes to Workers via KV, D1, or its own data store.

[[dispatch_namespaces]]
binding = "dispatcher"
namespace = "api-prod"
export default {
async fetch(request, env) {
try {
// parse the URL, read the subdomain
let worker_name = new URL(request.url).host.split('.')[0]
let user_worker = env.dispatcher.get(worker_name)
return user_worker.fetch(request)
} catch (e) {
if (e.message == 'Error: Worker not found.') {
// we tried to get a worker that doesn't exist in our dispatch namespace
return new Response('', {status: 404})
}
// this could be any other exception from `fetch()` *or* an exception
// thrown by the called worker (e.g. if the dispatched worker has
// `throw MyException()`, you could check for that here).
return new Response(e.message, {status: 500})
}
}
}
Dynamic Dispatch Namespaces and Unlimited Scripts
Traditional Service Bindings link two named Workers explicitly, which doesn't fit the Workers for Platforms model—User Workers are uploaded ad hoc. Dynamic Dispatch Namespaces solve this: a namespace is a collection of User Workers, and a Dispatch Worker can call any of them without pre-defining the relationship. This gives platform vendors the flexibility Service Bindings offer, minus the hard-coded connections.
Standard Workers accounts are limited to 100 scripts, which is inadequate for platforms with hundreds of end developers. Workers for Platforms removes this constraint—some customers deploy a fresh script on every code change from an end developer, maintaining version history and enabling fast rollbacks when a bug ships.
User Workers are uploaded to a namespace via the Cloudflare API (wrangler support is on the way). The snippet below shows a basic HTML form that collects a script and customer ID, then pushes the code to the desired namespace.
export default {
async fetch(request: Request) {
try {
// on form submit
if (request.method === "POST"){
const str = JSON.stringify(await request.json())
const upload_obj = JSON.parse(str)
await upload(upload_obj.customerID, upload_obj.script)
}
//render form
return new Response (html, {
headers: {
"Content-Type": "text/html"
}
})
} catch (e) {
// form error
return new Response(e.message, {status: 500})
}
}
}
async function upload(customerID:string, script:string){
const scriptName = customerID;
const scriptContent = script;
const accountId = "<ACCOUNT_ID>";
const dispatchNamespace = "api-prod";
const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/dispatch/namespaces/${dispatchNamespace}/scripts/${scriptName}`;
// construct and send request
const response = await fetch(url, {
method: "PUT",
body: scriptContent,
headers: {
"Content-Type": "application/javascript",
"X-Auth-Email": "<EMAIL>",
"X-Auth-Key": "<API_KEY>"
}
});
const result = (await response.json());
if (response.status != 200) {
throw new Error(`Upload error`);
}
}
The Cloudflare dashboard now also includes a Workers for Platforms UI, allowing vendors to inspect Dispatch Namespaces, search scripts, and view per-Worker analytics.

Availability and Roadmap
Workers for Platforms is currently limited to Cloudflare Enterprise plan customers. Interested parties should contact their CSM or start with the example application and starter project plus the developer documentation. Cloudflare plans to extend availability to the Workers Paid plan; updates will be announced in the Cloudflare Discord in the workers-for-platforms channel.
Meanwhile, early customers have already suggested roadmap items Cloudflare is prioritizing:
- Granular User Worker governance, including custom script limits and fetch allowlist/blocklist
- GraphQL API for fetching per-tag metrics on User Workers
- A plug-and-play Platform Development Kit
- Tighter integration with Cloudflare for SaaS custom domains



