Text in Expression Editor:
http.cookie contains "user_id"Selected operation under Modify request header: Set dynamic
Header name: Cloudflare-Workers-Version-Key
Value: http.request.cookies["user_id"][0]
During a gradual deployment, each request has a random chance of routing to either version based on the specified percentages. This means the same user can be served content from a different version every time a request is made, which can cause version skew issues.
Version affinity solves this by deterministically assigning users to a version based on a stable identifier, so they consistently hit the same version across page loads and subrequests for the duration of the gradual deployment.
Set the Cloudflare-Workers-Version-Key header on the incoming request to your Worker:
curl -s https://example.com -H 'Cloudflare-Workers-Version-Key: foo'For a given deployment, all requests with a version key set to foo will be handled by the same version of your Worker. The platform hashes the key and uses the result with the configured percentages to deterministically assign a version - you do not choose which version a key maps to.
As you progress a gradual deployment (for example, from 10% to 20% to 50%), users whose keys were already assigned to the new version will remain on it. Users on the old version will progressively move to the new version as the percentage increases, but will not flip back unless you roll back.
You can set the Cloudflare-Workers-Version-Key header both when making an external request from the Internet to your Worker, as well as when making a subrequest from one Worker to another Worker using a service binding.
Version affinity is particularly important when your Worker serves static assets with content-hashed filenames (like index-a1b2c3d4.js), which is the default behavior of most modern build tools and frameworks.
During a gradual rollout, different versions of your application will have different asset filenames:
assets/index-a1b2c3d4.jsassets/index-m3n4o5p6.jsWithout version affinity, a user can receive HTML from version A, but when their browser requests index-a1b2c3d4.js, that request may be routed to version B - which does not have that file - resulting in a 404 error and a broken page.
Configuring version affinity using any of the methods in Choose a version key prevents this entirely by ensuring all requests from the same user are routed to the same version.
The right version key depends on what stable identifiers your application has available. You can set the header using a Transform Rule on your zone, which extracts values from the request without modifying your application code.
If your application has a user identifier in a cookie or header, this is the best option. Each user is deterministically assigned to a version and stays there across sessions, devices, and reloads.
Text in Expression Editor:
http.cookie contains "user_id"Selected operation under Modify request header: Set dynamic
Header name: Cloudflare-Workers-Version-Key
Value: http.request.cookies["user_id"][0]
If your application sets a session cookie, use the session identifier. This gives consistent routing for the duration of the session. If the session expires and a new one is created, the user may be assigned to a different version.
Text in Expression Editor:
http.cookie contains "session_id"Selected operation under Modify request header: Set dynamic
Header name: Cloudflare-Workers-Version-Key
Value: http.request.cookies["session_id"][0]
If your application does not have any stable identifier in the request, you have two options:
Option 1: Use the client IP address. This is the simplest approach and requires no application changes. Users behind the same NAT or VPN will be grouped together, and mobile users who switch networks may change version, but for most applications this significantly reduces version flip-flopping compared to random per-request routing.
Text in Expression Editor:
trueSelected operation under Modify request header: Set dynamic
Header name: Cloudflare-Workers-Version-Key
Value: ip.src
Option 2: Set a long-lived cookie from your Worker. On the first request (which will be randomly assigned), your Worker generates a stable identifier and sets it as a cookie. All subsequent requests use that cookie as the version key. This gives the best consistency for anonymous users, at the cost of a small amount of application code.
export default { async fetch(request, env) { const response = await handleRequest(request, env);
// Set a long-lived cookie to use as a version affinity key. const COOKIE_NAME = "version-key"; // can be any name const cookieHeader = request.headers.get("Cookie") ?? ""; const hasAffinityCookie = new RegExp(`(?:^|;\\s*)${COOKIE_NAME}=`).test( cookieHeader, );
if (!hasAffinityCookie) { const id = crypto.randomUUID(); response.headers.append( "Set-Cookie", `${COOKIE_NAME}=${id}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=31536000`, ); }
return response; },};export default { async fetch(request: Request, env: Env): Promise<Response> { const response = await handleRequest(request, env);
// Set a long-lived cookie to use as a version affinity key. const COOKIE_NAME = "version-key"; // can be any name const cookieHeader = request.headers.get("Cookie") ?? ""; const hasAffinityCookie = new RegExp(`(?:^|;\\s*)${COOKIE_NAME}=`).test(cookieHeader);
if (!hasAffinityCookie) { const id = crypto.randomUUID(); response.headers.append( "Set-Cookie", `${COOKIE_NAME}=${id}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=31536000`, ); }
return response; },};Then create a Transform Rule to use this cookie as the version key:
Text in Expression Editor:
http.cookie contains "version-key"Selected operation under Modify request header: Set dynamic
Header name: Cloudflare-Workers-Version-Key
Value: http.request.cookies["version-key"][0]
You can verify that version affinity is working by sending multiple requests with the same version key and confirming they are handled by the same version:
# Both requests should return responses from the same versioncurl -s https://example.com -H 'Cloudflare-Workers-Version-Key: test-user-123'curl -s https://example.com -H 'Cloudflare-Workers-Version-Key: test-user-123'Use the version metadata binding to include the version ID in your Worker's response during testing.
During gradual rollouts, monitor your Worker's analytics for increased 404 response rates, especially for asset files (.js, .css, .png). Use Analytics Engine or Logpush to track these metrics and catch version skew issues early. If you notice problems, you can roll back to the previous version.