You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Skew Protection ensures that client-side JavaScript code continues to communicate with the same version of your Worker that originally served it, preventing version mismatch errors during deployments.
10
10
@@ -13,69 +13,325 @@ Version skew occurs when a user's browser has loaded JavaScript from one version
13
13
Cloudflare's Skew Protection resolves this problem automatically by ensuring all requests from a client session are routed to the same Worker version that served the initial page load. This works seamlessly with gradual deployments, allowing you to safely roll out new versions without breaking active user sessions.
14
14
15
15
### How it works
16
+
Skew Protection keeps multiple versions of your Worker deployed simultaneously. Your application includes version information in each request, and Cloudflare automatically routes those requests to the matching Worker version.
16
17
17
-
Skew Protection works automatically for all requests without requiring any configuration or code changes. Cloudflare handles version consistency at the platform level for both client-to-server requests and Worker-to-Worker service binding calls.
18
+
When Skew Protection is enabled on a Worker,
19
+
-**Automatic version routing**: Requests are automatically routed to specific versions using the `Cloudflare-Workers-Version-Overrides` header or `?dpl=` query parameter
20
+
-**Configurable version TTL**: Versions remain routable for a configurable period (default: 2 days) after deployment, ensuring users on older versions can complete their sessions
21
+
-**Version cutoff**: Manually mark a specific version as the cutoff to immediately make all earlier versions unroutable, useful for critical security fixes or breaking changes
22
+
-**Version control**: Disable routing to individual faulty versions without affecting other versions in the TTL window
18
23
19
-
#### Automatic version tracking for clients
24
+
#### Framework support
25
+
If you're using one of the following frameworks with their official Cloudflare adapters, Skew Protection will be automatically enabled by default during the build process:
26
+
- SvelteKit
27
+
- Remix
28
+
- ...
20
29
21
-
When a user first loads your application, Cloudflare automatically:
30
+
These adapters automatically configure Skew Protection and implement the routing logic. If you'd like to disable skew protection, you can do so.
22
31
23
-
1. Sets a version cookie (`__cf_worker_version`) containing the deployment ID that served the initial page load
24
-
2. Includes this cookie in all subsequent requests from the same browser session
25
-
3. Routes requests to the same Worker version that set the cookie
26
-
4. Maintains old deployments for active sessions during gradual rollouts
32
+
#### Version Identification
27
33
28
-
#### Skew protection during a gradual rollout
34
+
Cloudflare identifies which version to route requests to using the following mechanisms, in priority order:
29
35
30
-
1. User visits your site and gets Version A (latest deployment)
- Example for multiple workers: `Cloudflare-Workers-Version-Overrides: worker-a="v123", worker-b="v456"`
39
48
40
-
### Service bindings and version consistency
49
+
Query parameters take priority over headers when both are present. This ensures maximum compatibility since query parameters work universally for all request types (including static assets loaded by the browser via HTML tags), while headers only work for programmatic requests where you control the fetch call.
41
50
42
-
When using service bindings to connect multiple Workers, Skew Protection ensures version consistency across the entire chain of Worker calls.
51
+
:::note
52
+
Cloudflare's Skew Protection does not use cookies for version tracking. Cookies break multi-tab usage: if a user opens your site in two tabs and refreshes one tab to get a new version, the cookie forces the other tab to also switch versions, causing errors. Query parameters and headers avoid this problem by letting each tab maintain its own version independently.
53
+
:::
43
54
44
-
#### How version cookies work with service bindings
55
+
#### Request routing
45
56
46
-
**On the initial page load:**
57
+
When a request includes version routing information:
47
58
48
-
1. Client requests Worker A
49
-
2. Worker A sets `__cf_worker_version_worker_a` cookie
50
-
3. Worker A calls Worker B
51
-
4. Worker B sets `__cf_worker_version_worker_b` cookie in response
Cloudflare will attempt to route the request to the specified version. The routing behavior is:
56
63
57
-
1. Client sends both `__cf_worker_version_worker_a` and `__cf_worker_version_worker_b` cookies
58
-
2. Worker A reads its own cookie to select the correct version
59
-
3. Worker A extracts Worker B's cookie and includes it when calling Worker B
60
-
4. Worker B reads its cookie to select the correct version
64
+
**If the version is routable** (within TTL, after cutoff, not disabled):
65
+
- Request is routed to the specified version
61
66
62
-
This ensures that both Worker A and Worker B maintain version consistency with the original deployment that served the client, even when they are deployed independently.
67
+
**If the version is NOT routable** (expired, before cutoff, or manually disabled):
68
+
- Request is **automatically rerouted to the active deployment**
69
+
- No error is returned to the user
70
+
- This ensures graceful degradation when cached version IDs become invalid
63
71
64
-
### Session expiration and version cleanup
65
-
Sessions naturally expire after 12 hours (or your configured duration):
72
+
### Enable Skew Protection
66
73
67
-
- When a session expires, the user's next request creates a new session with current versions
68
-
- Old Worker versions remain available as long as there are active sessions using them
69
-
- Once all sessions using an old version have expired, that version can be safely deleted
74
+
You can enable Skew Protection via API, Dashboard, or Wrangler.
70
75
71
-
## Setting a version cutoff
76
+
**Configuration Options:**
77
+
-`enabled`: Enable or disable Skew Protection for the Worker
78
+
-`version_ttl_hours`: Number of hours versions remain routable after deployment. Default: 48 hours.
72
79
73
-
For critical security fixes, you can set a version cutoff to immediately stop serving older versions.
-`cutoff_timestamp`: When the version cutoff was enabled
168
+
-`unroutable_versions`: Array of versions that were previously routable but are now unroutable due to the cutoff
169
+
- Each affected version shows its `previous_status` and `new_status`
170
+
171
+
:::note
172
+
173
+
Requests with the `Cloudflare-Workers-Version-Overrides` header or `?dpl= query` parameter targeting versions before the cutoff will be automatically rerouted to the active deployment instead of failing. This ensures that users don't experience errors when their cached version IDs become invalid.
If you maintain a Cloudflare adapter for a framework, you can implement Skew Protection by using the version ID that is exposed in the Worker.
207
+
208
+
Skew Protection has two parts that work together:
209
+
-**Server-side:** Your Worker reads its version ID and sends it to the browser
210
+
-**Client-side:** Browser code reads the version ID and includes it in all subsequent requests back to the Worker
211
+
212
+
### Server-Side Implementation
213
+
214
+
**Step 1: Expose Worker version information**
215
+
216
+
Configure the Worker to expose runtime information it needs for skew protection. This will require exposing:
217
+
- Version ID: This will be exposed through the version metadata binding and will provide access to the Worker's version ID.
218
+
- Worker name: The name of your Worker. This will be used in the header sent on the client side.
219
+
220
+
The version metadata binding will expose the Worker's current version ID at runtime through `env.CF_VERSION_METADATA`. Your adapter should add this to the Wrangler config file during the build process.
221
+
222
+
For skew protection, you only need the `id` field.
223
+
224
+
Additionally, you will need to expose the name of the Worker. You can do this by setting the Worker name as an environment variable.
225
+
226
+
<WranglerConfig>
227
+
228
+
```toml
229
+
# Expose version metadata as CF_VERSION_METADATA binding
230
+
[version_metadata]
231
+
binding = "CF_VERSION_METADATA"
232
+
233
+
# Set the worker name as an environment variable
234
+
[vars]
235
+
WORKER_NAME = "my-app-worker"
236
+
```
237
+
</WranglerConfig>
238
+
239
+
**Step 2: Read version ID and Worker name in the Worker**
240
+
241
+
In your Worker's `fetch` handler, read the version ID and Worker name from the binding, so you can send it to the client.
242
+
243
+
```ts
244
+
exportdefault {
245
+
async fetch(request, env, ctx) {
246
+
// Read version ID
247
+
const versionId =env.CF_VERSION_METADATA.id;
248
+
249
+
// Pass it to your framework's render function
250
+
const html =awaitframework.render(request, { versionId });
251
+
}
252
+
};
253
+
```
254
+
**Step 3: Inject Version ID and Worker name Into HTML**
255
+
256
+
Insert the version ID and Worker name into your HTML response so the client can read it. This should happen After your framework renders the HTML, but before returning the response.
257
+
258
+
**Example server-side implementation**
259
+
```ts
260
+
exportdefault {
261
+
async fetch(request, env, ctx) {
262
+
// Read version ID and worker name
263
+
const versionId =env.CF_VERSION_METADATA.id;
264
+
const workerName =env.WORKER_NAME;
265
+
266
+
// Render your framework's application
267
+
let html =awaitrenderFramework(request);
268
+
269
+
// Inject version ID and worker name into HTML
270
+
html=html.replace('</head>',
271
+
`<script>
272
+
window.__CF_WORKER_VERSION__="${versionId}";
273
+
window.__CF_WORKER_NAME__="${workerName}";
274
+
</script></head>`
275
+
);
276
+
277
+
returnnewResponse(html, {
278
+
headers: {
279
+
'Content-Type': 'text/html'
280
+
}
281
+
});
282
+
}
283
+
};
284
+
```
285
+
286
+
### Client-side implementation
287
+
288
+
The client side is responsible for reading the version information that the server injected into the HTML and including it in all subsequent requests back to the Worker. This ensures that once a user loads a page, all their API calls and asset requests continue to talk to the same Worker version.
289
+
290
+
**Understanding what the client needs to do**
291
+
292
+
When the page loads, the browser needs to:
293
+
1. Read the version ID and worker name that the server injected
294
+
2. Intercept all `fetch()` calls to add the version header
295
+
3. Modify dynamic asset URLs to add the`?dpl=` query parameter
296
+
297
+
#### Understanding the version header format
298
+
The `Cloudflare-Workers-Version-Overrides header` uses a Dictionary Structured Header as the format.
When Cloudflare receives a request with this header:
306
+
1. It parses the worker name and version ID
307
+
2. Checks that it can route the request to this version of the Worker
308
+
3. If the version specified is "unroutable" then the request will be routed to the current deployment
309
+
310
+
*Handling static assets**
311
+
The `Cloudflare-Workers-Version-Overrides` header only works for fetch requests. For static assets loaded by HTML tags like `<img>, <link>, and <script>`, browsers don't allow you to add custom headers. Instead, you must add the version as a query parameter: `?dpl=<version-id>`
0 commit comments