Skip to content
Cloudflare Docs

Versioning

Versioning lets your users upload new code without immediately serving it to production traffic. You control the rollout strategy, whether that is instant deployment, gradual percentage-based releases, or custom routing logic for previews and testing.

Control routing

You have two ways to control how requests reach a user's Worker version:

  1. Version-specific routing: Pass a versionId or versionAlias to the dispatcher to pick the exact build that handles the request. Use this for previews, tests, or custom logic.
  2. Managed deployments: Let the platform route traffic automatically based on the active deployment. Configure deployments to send all traffic to one version or split it between two versions by percentage.

Creating a new version

When a user uploads new code to your platform, you'll create a new version of their Worker. This is where you can assign an alias to make it easier to reference later.

Terminal window
curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/{namespace_name}/scripts/{script_name}/versions" \
-H "Authorization: Bearer {api_token}" \
-H "Content-Type: multipart/form-data" \
-F "metadata={\"main_module\":\"worker.js\",\"annotations\":{\"workers/alias\":\"staging\"}}" \
-F "files=@worker.js;type=application/javascript+module"

he API returns the new version details:

{
"result": {
"id": "bcf64294-7b3b-45e1-8f4e-e8e8c1c7cf7c",
"number": 5,
"metadata": {
"created_on": "2024-01-01T00:00:00.000000Z",
"modified_on": "2024-01-01T00:00:00.000000Z",
"source": "api"
},
"resources": {
"script": {...},
"bindings": [...]
}
}
}

Version-specific routing

Version-specific routing gives you full control over which version handles each request. The dispatcher returns the requested version regardless of the active deployment, letting you run multiple versions of the same Worker in parallel when needed.

In the dispatcher, you can route requests to specific Worker versions by referencing the alias you assigned during version creation:

JavaScript
const worker = env.DISPATCHER.get("customer-worker", { versionAlias: "staging" });
return worker.fetch(request);

Since aliases are predictable (you set them), this is often simpler than tracking version IDs.

Using version IDs

You can also route to specific versions by their ID:

JavaScript
const worker = env.DISPATCHER.get("customer-worker", { versionId: "abc123" });
return worker.fetch(request);

Managed deployments

You can also use the deployment endpoints to define how traffic should be distributed. When your dispatcher calls env.DISPATCHER.get("worker-name") without a versionId, it automatically routes requests according to the active deployment you configured.

JavaScript
// Routes traffic based on the active deployment settings.
const worker = env.DISPATCHER.get("customer-worker");
return worker.fetch(request);

When you configure a deployment, you have two options:

  1. Route 100% of traffic to a single version: Push a new version live or roll back instantly by pointing the deployment at one version_id.
  2. Split traffic between two versions for gradual rollouts: De-risk a release by splitting traffic between the new version and the previous one. Your dispatcher code stays the same, while Cloudflare sends the configured percentage to each version.
{
"strategy": "percentage",
"versions": [
{
"version_id": "new-version-abc123",
"percentage": 20
},
{
"version_id": "old-version-xyz789",
"percentage": 80
}
]
}

With this setup, roughly 20% of incoming requests will be handled by new-version-abc123, while the other 80% continue to use old-version-xyz789.

Platform implementation patterns

Use version-specific routing and deployments together to build features for your users.

Preview environments

Let users test code changes before deploying to production. When they upload new code, assign a version alias like pr-123. Since you control the alias at creation time, you can use it directly in your routing without needing to store or look up the version ID.

You can serve previews from your platform domain using subdomains that include the version alias, such as pr-123.customer-name.platform-preview.com:

JavaScript
export default {
async fetch(request, env) {
const url = new URL(request.url);
const subdomain = url.hostname.split(".")[0];
// Check if this is a preview subdomain (e.g., staging.customer-name.yourdomain.com)
const knownAliases = ["staging", "preview", "dev"];
if (knownAliases.includes(subdomain)) {
const customerName = url.hostname.split(".")[1]; // Extract customer identifier
const worker = env.DISPATCHER.get(customerName, { versionAlias: subdomain });
return worker.fetch(request);
}
// Normal requests follow the active deployment
const userId = subdomain; // For production: customer-name.yourdomain.com
const worker = env.DISPATCHER.get(userId);
return worker.fetch(request);
}
};

This pattern lets your users access different versions of their Worker through predictable URLs without needing custom DNS configuration on their end.

Managing gradual rollouts

Enable your users to gradually rollout changes by orchestrating the gradual deployment on their behalf. Here's what this would look like:

  1. A user uploads a new version through your platform's interface.
  2. Your backend initiates the rollout by calling the deployment API to send some percentage of traffic (e.g. 5%) to the new version.
  3. The user controls the rollout by adjusting traffic percentages in your interface. Each adjustment will trigger an API call from your backend to update the deployment.

The dispatcher will continue using env.DISPATCHER.get("worker-name") to call the Worker and automatically route requests across the two versions.

Version affinity

Gradual deployments operate on a per-request basis: each request has a chance of hitting any version listed in the deployment. If different requests share state or expect compatible schemas, reduce version skew by pinning specific identifiers to a version. For background, review Workers gradual deployments.

JavaScript
export default {
async fetch(request, env) {
const userId = getUserIdFromRequest(request);
const workerName = getWorkerNameForUser(userId);
// Determine which version this user should use
const userVersion = await determineUserVersion(userId);
if (userVersion) {
// Route this user consistently to their assigned version
const worker = env.DISPATCHER.get(workerName, { versionId: userVersion });
return worker.fetch(request);
}
// Otherwise use the active deployment percentages
const worker = env.DISPATCHER.get(workerName);
return worker.fetch(request);
}
};

This keeps all requests from the same identifier on the same Worker version throughout a session, even while a gradual rollout is in progress.