From 262cd9c8d7ea3fad01e5a80fa91588cd359e9b61 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 24 Jun 2025 16:44:56 +0200 Subject: [PATCH 1/4] docs(cloudflare): skew protection --- pages/cloudflare/howtos/_meta.json | 3 +- pages/cloudflare/howtos/skew.mdx | 79 ++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 pages/cloudflare/howtos/skew.mdx diff --git a/pages/cloudflare/howtos/_meta.json b/pages/cloudflare/howtos/_meta.json index c8fe30e..5a98a13 100644 --- a/pages/cloudflare/howtos/_meta.json +++ b/pages/cloudflare/howtos/_meta.json @@ -6,5 +6,6 @@ "image": "Image Optimization", "custom-worker": "Custom Worker", "keep_names": "__name issues", - "workerd": "workerd specific packages" + "workerd": "workerd specific packages", + "skew": "Skew Protection" } diff --git a/pages/cloudflare/howtos/skew.mdx b/pages/cloudflare/howtos/skew.mdx new file mode 100644 index 0000000..a75c545 --- /dev/null +++ b/pages/cloudflare/howtos/skew.mdx @@ -0,0 +1,79 @@ +import { Callout } from "nextra/components"; + +## Skew protection + +The Cloudflare adapter has _experimental support_ for skew protection based on [the preview URLs](https://developers.cloudflare.com/workers/configuration/previews/). + + + Preview URLs are disabled for [Workers that implement a Durable + Object](https://developers.cloudflare.com/workers/configuration/previews/#limitations). If your app uses + Durable Objects, they will need to be implemented in a separate Worker. + + +### How to enable the skew protection + +**OpenNext config** + +Set `cloudflare.skewProtectionEnabled` to `true` to enable skew protection. + +```ts +// open-next.config.ts +export default { + // ... + cloudflare: { + skewProtectionEnabled: true, + }, +} satisfies OpenNextConfig; +``` + +**Wrangler configuration** + +The Worker needs to serve the correct version of the app assets. For that it need be be executed before incoming requests are matched against the assets. Set [`run_worker_first`](https://developers.cloudflare.com/workers/static-assets/binding/#run_worker_first) in your wrangler configuration to enable this behavior: + +```jsonc +// wrangler.jsonc +{ + "name": "my-app", + // ... + "assets": { + "directory": ".open-next/assets", + "binding": "ASSETS", + "run_worker_first": true, + }, + // ... +} +``` + +**Environment variables** + +The following environment variables should be set when the skew protection is use: + +- `CF_WORKER_NAME` should be set to the name of the worker, i.e. `my-app` given the config above +- `CF_PREVIEW_DOMAIN` is the the subdomain of `workers.dev` where the previews are deployed, i.e. `..workers.dev` +- `CF_WORKERS_SCRIPTS_API_TOKEN` is an API token with the `Workers Scripts:Read` permission +- `CF_ACCOUNT_ID` is the Cloudflare account id where the app is deployed. + +Those variables are used to retrieve the past deployments of your application before the app is deployed. + +**Next config** + +You must set a different `deploymentId` in your next config each time your app is deployed. You will get an error if the `deployementId` has already been used when deploying. + +The cloudflare adapter exports a `getDeploymentId()` function that can be used to generate a unique deployment id. + +```ts +// next.config.ts +import { getDeploymentId } from "@opennextjs/cloudflare"; + +const nextConfig = { + // ... + deploymentId: getDeploymentId(), +}; +``` + +### What you should know + +- Because the Worker is configured to run in front of the assets Worker (`run_worker_first`), requesting an asset will count as a request to your Worker +- Requesting an older deployment will generate 2 requests: the request to the latest version and the request to the older version +- It is not currently possible to delete a deployment +- Requests to an older deployement will be a few milli-seconds slower than requests to the latest version of the app From 8719e572c4566f86c59b79629bbe5f362033c5dc Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Wed, 9 Jul 2025 16:08:09 +0200 Subject: [PATCH 2/4] fixup! sync skew protection, add assets --- pages/cloudflare/howtos/_meta.json | 3 +- pages/cloudflare/howtos/assets.mdx | 56 ++++++++++++++++++++++++++++++ pages/cloudflare/howtos/skew.mdx | 20 +++++++---- 3 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 pages/cloudflare/howtos/assets.mdx diff --git a/pages/cloudflare/howtos/_meta.json b/pages/cloudflare/howtos/_meta.json index 5a98a13..c2190c7 100644 --- a/pages/cloudflare/howtos/_meta.json +++ b/pages/cloudflare/howtos/_meta.json @@ -7,5 +7,6 @@ "custom-worker": "Custom Worker", "keep_names": "__name issues", "workerd": "workerd specific packages", - "skew": "Skew Protection" + "skew": "Skew Protection", + "assets": "Static assets" } diff --git a/pages/cloudflare/howtos/assets.mdx b/pages/cloudflare/howtos/assets.mdx new file mode 100644 index 0000000..6c05ebf --- /dev/null +++ b/pages/cloudflare/howtos/assets.mdx @@ -0,0 +1,56 @@ +## Static assets (public folder) + +The [static assets of Next located in the public folder](https://nextjs.org/docs/app/api-reference/file-conventions/public-folder) are served using [Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/). + +Workers Static Assets can intercept requests to the application ([`run_worker_first=false`](https://developers.cloudflare.com/workers/static-assets/) which is the default). This is the most cost efficient option as asset requests will not be billed in that case. + +Another option is to run the Worker first (`run_worker_first=true`) to offer more flexibility at the expense of a higher cost. + +Note that `run_worker_first` could also be set to a list of patterns - this is a great option if you need more flexibility for only a subset of the statids assets. + +### run_worker_first=false + +When `run_worker_first` is set to `false`, requests are intercepted before reaching the worker and are not billed: + +```jsonc +// wrangler.jsonc +{ + "name": "my-app", + // ... + "assets": { + "directory": ".open-next/assets", + "binding": "ASSETS", + // Optional as false is the default value + "run_worker_first": false, + }, + // ... +} +``` + +This is the most cost efficient option to use when you do not need to serve assets behind the middleware or Next rewrites and headers from [the Next config](https://nextjs.org/docs/app/api-reference/config/next-config-js). + +When `run_worker_first=false` you can still configure [headers](https://developers.cloudflare.com/workers/static-assets/headers/) and [redirects](https://developers.cloudflare.com/workers/static-assets/redirects/) via Worker Static Assets. + +### run_worker_first=true + +When `run_worker_first` is set to `true`, all the requests will reach the Worker and be billed: + +```jsonc +// wrangler.jsonc +{ + "name": "my-app", + // ... + "assets": { + "directory": ".open-next/assets", + "binding": "ASSETS", + "run_worker_first": true, + }, + // ... +} +``` + +The Open Next asset resolver will be used to retrieve the assets from the Worker. + +When `run_worker_first=true`, assets are served behind the middleware and Next rewrites and headers from [the Next config](https://nextjs.org/docs/app/api-reference/config/next-config-js). The [headers](https://developers.cloudflare.com/workers/static-assets/headers/) and [redirects](https://developers.cloudflare.com/workers/static-assets/redirects/) configured for the Worker Static Assets do not apply in this case. + +`run_worker_first=true` should be used if you plan to use skew protection. diff --git a/pages/cloudflare/howtos/skew.mdx b/pages/cloudflare/howtos/skew.mdx index a75c545..ff1eef1 100644 --- a/pages/cloudflare/howtos/skew.mdx +++ b/pages/cloudflare/howtos/skew.mdx @@ -14,21 +14,29 @@ The Cloudflare adapter has _experimental support_ for skew protection based on [ **OpenNext config** -Set `cloudflare.skewProtectionEnabled` to `true` to enable skew protection. +Set `cloudflare.skewProtection.enabled` to `true` to enable skew protection: ```ts // open-next.config.ts export default { // ... cloudflare: { - skewProtectionEnabled: true, + skewProtection: { + enabled: true, + // Maximum number of previous versions to use. + // Optional, default to 20. + maxNumberOfVersions: 20, + // Age of the oldest version to use (from the last deplyment date) + // Optional, default to 7 days. + maxVersionAgeDays: 7, + }, }, } satisfies OpenNextConfig; ``` **Wrangler configuration** -The Worker needs to serve the correct version of the app assets. For that it need be be executed before incoming requests are matched against the assets. Set [`run_worker_first`](https://developers.cloudflare.com/workers/static-assets/binding/#run_worker_first) in your wrangler configuration to enable this behavior: +The Worker needs to serve the correct version of the app assets. For that it need be be executed before incoming requests are matched against the assets. Set [`run_worker_first`](https://developers.cloudflare.com/workers/static-assets/binding/#run_worker_first) to `tue` in your wrangler configuration to enable this behavior: ```jsonc // wrangler.jsonc @@ -46,18 +54,18 @@ The Worker needs to serve the correct version of the app assets. For that it nee **Environment variables** -The following environment variables should be set when the skew protection is use: +The following environment variables should be set when the skew protection is used: - `CF_WORKER_NAME` should be set to the name of the worker, i.e. `my-app` given the config above - `CF_PREVIEW_DOMAIN` is the the subdomain of `workers.dev` where the previews are deployed, i.e. `..workers.dev` - `CF_WORKERS_SCRIPTS_API_TOKEN` is an API token with the `Workers Scripts:Read` permission - `CF_ACCOUNT_ID` is the Cloudflare account id where the app is deployed. -Those variables are used to retrieve the past deployments of your application before the app is deployed. +Those variables are used to retrieve the past deployments of your application. **Next config** -You must set a different `deploymentId` in your next config each time your app is deployed. You will get an error if the `deployementId` has already been used when deploying. +You must set a different `deploymentId` in your next config each time your app is deployed. You will get an error if the `deployementId` has already been used by a previous deployment. The cloudflare adapter exports a `getDeploymentId()` function that can be used to generate a unique deployment id. From 94519156fbf05410c351f79fab9ca685d0feb8ee Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 10 Jul 2025 10:13:44 +0200 Subject: [PATCH 3/4] Update pages/cloudflare/howtos/skew.mdx Co-authored-by: conico974 --- pages/cloudflare/howtos/skew.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/cloudflare/howtos/skew.mdx b/pages/cloudflare/howtos/skew.mdx index ff1eef1..f3c748d 100644 --- a/pages/cloudflare/howtos/skew.mdx +++ b/pages/cloudflare/howtos/skew.mdx @@ -56,7 +56,7 @@ The Worker needs to serve the correct version of the app assets. For that it nee The following environment variables should be set when the skew protection is used: -- `CF_WORKER_NAME` should be set to the name of the worker, i.e. `my-app` given the config above +- `CF_WORKER_NAME` should be set to the name of the worker, i.e. `my-app` given the config above. If you're using environment, the name of the app should contain your env, i.e. `my-app-` - `CF_PREVIEW_DOMAIN` is the the subdomain of `workers.dev` where the previews are deployed, i.e. `..workers.dev` - `CF_WORKERS_SCRIPTS_API_TOKEN` is an API token with the `Workers Scripts:Read` permission - `CF_ACCOUNT_ID` is the Cloudflare account id where the app is deployed. From f0ce13647f7e4d576f9fcac1d04a34945e9faf26 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 10 Jul 2025 10:13:54 +0200 Subject: [PATCH 4/4] Update pages/cloudflare/howtos/skew.mdx Co-authored-by: conico974 --- pages/cloudflare/howtos/skew.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pages/cloudflare/howtos/skew.mdx b/pages/cloudflare/howtos/skew.mdx index f3c748d..d10f3eb 100644 --- a/pages/cloudflare/howtos/skew.mdx +++ b/pages/cloudflare/howtos/skew.mdx @@ -84,4 +84,5 @@ const nextConfig = { - Because the Worker is configured to run in front of the assets Worker (`run_worker_first`), requesting an asset will count as a request to your Worker - Requesting an older deployment will generate 2 requests: the request to the latest version and the request to the older version - It is not currently possible to delete a deployment -- Requests to an older deployement will be a few milli-seconds slower than requests to the latest version of the app +- Requests to an older deployment will be a few milli-seconds slower than requests to the latest version of the app +- Request to a deployment older than the `maxNumberOfVersions` or older than `maxVersionAgeDays` will fallback to the current deployment