From 88d1abff9ca9e7ae0c88ca251fc48b317879357d Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 9 Oct 2025 14:53:26 +0200 Subject: [PATCH 1/9] docs(cloudflare): perf tips --- pages/cloudflare/_meta.json | 1 + pages/cloudflare/caching.mdx | 16 ++++- pages/cloudflare/perf.mdx | 88 ++++++++++++++++++++++++++++ pages/cloudflare/troubleshooting.mdx | 8 --- 4 files changed, 102 insertions(+), 11 deletions(-) create mode 100644 pages/cloudflare/perf.mdx diff --git a/pages/cloudflare/_meta.json b/pages/cloudflare/_meta.json index de316a1..d5204d4 100644 --- a/pages/cloudflare/_meta.json +++ b/pages/cloudflare/_meta.json @@ -6,6 +6,7 @@ "caching": "", "howtos": "How-Tos", "examples": "", + "perf": "Perfomance Tips", "community": "Community projects", "known-issues": "Known issues", "troubleshooting": "", diff --git a/pages/cloudflare/caching.mdx b/pages/cloudflare/caching.mdx index 8ba58d8..c316d8b 100644 --- a/pages/cloudflare/caching.mdx +++ b/pages/cloudflare/caching.mdx @@ -191,7 +191,11 @@ import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; import { purgeCache } from "@opennextjs/cloudflare/overrides/cache-purge/index"; export default defineCloudflareConfig({ - incrementalCache: withRegionalCache(r2IncrementalCache, { mode: "long-lived" }), + incrementalCache: withRegionalCache(r2IncrementalCache, { + mode: "long-lived", + // Setting `bypassTagCacheOnCacheHit` to `true` requires enabling cache purge + bypassTagCacheOnCacheHit: true, + }), queue: doQueue, // This is only required if you use On-demand revalidation tagCache: doShardedTagCache({ @@ -264,7 +268,7 @@ Next.js also generates _immutable_ files that don't change between builds. Those There are 3 storage options for the incremental cache: - **R2 Object Storage:** A [cost-effective](https://developers.cloudflare.com/r2/pricing/) S3-compatible object storage option for large amounts of unstructured data. Data is stored in a single region, meaning cache interactions may be slower - this can be mitigated with a regional cache. -- **Workers KV:** A [fast](https://blog.cloudflare.com/faster-workers-kv) key value store, it uses Cloudflare's [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/) to increase cache hit rates. When you write cached data to Workers KV, you write to storage that can be read by any Cloudflare location. This means your app can fetch data, cache it in KV, and then subsequent requests anywhere around the world can read from this cache. +- **Workers KV:** A [fast](https://blog.cloudflare.com/faster-workers-kv) key value store, it uses Cloudflare's [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/) to increase cache hit rates. When you write cached data to Workers KV, you write to storage that can be read by any Cloudflare location. This means your app can fetch data, cache it in KV, and then subsequent requests anywhere around the world can read from this cache. We do not recommend using KV because it is eventually consistent. - **Workers Static Assets:** A read-only store for the incremental cache, serving build-time values from [Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/). Revalidation is not supported with this cache. @@ -329,7 +333,7 @@ import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental- export default defineCloudflareConfig({ incrementalCache: withRegionalCache(r2IncrementalCache, { mode: "long-lived", - shouldLazilyUpdateOnCacheHit: true, + bypassTagCacheOnCacheHit: true, }), // ... }); @@ -751,3 +755,9 @@ If you want to use the durable object option, you will need to add the following ``` You can customize the duration of the cache purge buffering with the `NEXT_CACHE_DO_PURGE_BUFFER_TIME_IN_SECONDS` environment variable. The default is 5 seconds. It works by buffering the purge requests for a given amount of time and then sending them all at once. This is useful to avoid hitting the API rate limits. + +### Debugging + +You can add `NEXT_PRIVATE_DEBUG_CACHE=1` to your app `.env` file to debug any cache issue. +The app will output logs whenever the cache is accessed - those logs are generated by both Next and the cache adapter. +You can find more details in [the Next documentation](https://nextjs.org/docs/app/guides/incremental-static-regeneration#verifying-correct-production-behavior) diff --git a/pages/cloudflare/perf.mdx b/pages/cloudflare/perf.mdx new file mode 100644 index 0000000..b81e18d --- /dev/null +++ b/pages/cloudflare/perf.mdx @@ -0,0 +1,88 @@ +import { Callout } from "nextra/components"; + +## Performance tips + +They are a lot fo features and knobs you can tune to imrpve the performance of your app. This pages list some of the recommend settings. + +### General + +You should regularly update to the latest available version of [`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) to benefit from the latest performance and security updates. + +### Caching + +Cacing can drastically improves the performance of your application by only fetching data / re-generating your pages when they change. + +To get the most out of caching, you should start by reading how Next.js implements caching. The ["Caching in Next.js" guide](https://nextjs.org/docs/app/guides/caching) is a good place to start. + +You should then check [how to configure caching](/cloudflare/caching) for the OpenNext adapter. See below for some advice. + +#### [Incremental Cache](/cloudflare/caching#incremental-static-regeneration-isr) + +You should use the Workers Static Assets based cache if your site is SSG. It is the fastest available option. Note that as Workers Static Assets are read-only, this option can not be used with revalidation. + +When your app uses re-validation, use the R2 based store instead. We recommend the following settings to get the best perfomance: + +- use regional cache by wrapping the handler into `withRegionalCache(...)` + - use the `long-lived` mode + - you should not need to explictely set `shouldLazilyUpdateOnCacheHit` nor `bypassTagCacheOnCacheHit` as they are set to the most performant mode by default +- setup [automatic cache purge](/cloudflare/caching#automatic-cache-purge) + +Using KV is not recommended as it is eventually consistent. + +#### [Tag Cache](/cloudflare/caching#tag-cache-for-on-demand-revalidation) + +The Tag should be configured for App-Router based app using `revalidateTag` or `revalidatePath`. + +The D1 based tag cache should only be used if your site receives low traffic. The Durable Object based tag cache is the recommend option for most sites. See [the reference guide](/cloudflare/caching#tag-cache-for-on-demand-revalidation) for the available configuration options. + +Application using `revalidateTag` exclusively (and not `revalidatePath`) will benefit from using the `withFilter` wrapper with the `softTagFilter` filter. + +#### [Static Assets](/cloudflare/caching#static-assets-caching) + +You should add a `public/_headers` file to cache the static assets served by your app. + +See the [Cloudflare documentation](https://developers.cloudflare.com/workers/static-assets/headers/) for a detailed explanation of the default and the syntax. + +### Troubleshoting performance + +You can profile the code to troubleshot performance issues. + +#### Building unminified code + +Code profiles are much easier to read when the code is not minified, you can use the following settings to generate unminified code + +**next.config.ts** + +```ts +const nextConfig = { + // ... + experimental: { + serverMinification: false, + }, + webpack: (config) => { + config.optimization.minimize = false; + return config; + }, + compress: false, +}; +``` + +**CLI** + +Use the `--noMinify` option when building the app: + +```bash +opennextjs-cloudflare build --noMinify +``` + +#### Record a profile + +You should first launch a local version of your application by running + +```bash +opennextjs-cloudflare preview +``` + +Once the app is running, follow [the steps from the Workers doc](https://developers.cloudflare.com/workers/observability/dev-tools/cpu-usage/) to record a CPU profile. + +You can then inspect the profile and check if any particular section of the application is unexpectedly slow. diff --git a/pages/cloudflare/troubleshooting.mdx b/pages/cloudflare/troubleshooting.mdx index 686206d..1869bde 100644 --- a/pages/cloudflare/troubleshooting.mdx +++ b/pages/cloudflare/troubleshooting.mdx @@ -2,14 +2,6 @@ import { Callout } from "nextra/components"; ## Troubleshooting -### Trying to deploy to Cloudflare Pages, instead of Cloudflare Workers? - -`@opennextjs/cloudflare` is specifically built for deploying Next.js apps to [Cloudflare Workers](https://developers.cloudflare.com/workers/) - -Cloudflare Workers now support the majority of functionality from Cloudflare Pages, and have features that are not yet supported by Cloudflare Pages. Refer to the [Compatibility Matrix](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/) in the Cloudflare Workers docs. - -If you need to deploy to Cloudflare Pages, you can use `@cloudflare/next-on-pages`, and follow the [Cloudflare Pages guides for deploying Next.js apps](https://developers.cloudflare.com/pages/framework-guides/nextjs/). - ### "Your Worker exceeded the size limit of 3 MiB" The Cloudflare Account you are deploying to is on the Workers Free plan, which [limits the size of each Worker to 3 MiB](https://developers.cloudflare.com/workers/platform/limits/#worker-size). When you subscribe to the Workers Paid plan, each Worker can be up to 10 MiB. From dcafe08533ad676c4c292353382f04a03df19615 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 9 Oct 2025 20:31:16 +0200 Subject: [PATCH 2/9] Update pages/cloudflare/perf.mdx Co-authored-by: khuezy --- pages/cloudflare/perf.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/cloudflare/perf.mdx b/pages/cloudflare/perf.mdx index b81e18d..090b706 100644 --- a/pages/cloudflare/perf.mdx +++ b/pages/cloudflare/perf.mdx @@ -24,7 +24,7 @@ When your app uses re-validation, use the R2 based store instead. We recommend t - use regional cache by wrapping the handler into `withRegionalCache(...)` - use the `long-lived` mode - - you should not need to explictely set `shouldLazilyUpdateOnCacheHit` nor `bypassTagCacheOnCacheHit` as they are set to the most performant mode by default + - you should not need to explicitly set `shouldLazilyUpdateOnCacheHit` nor `bypassTagCacheOnCacheHit` as they are set to the most performant mode by default - setup [automatic cache purge](/cloudflare/caching#automatic-cache-purge) Using KV is not recommended as it is eventually consistent. From 9cbdb74ddf81cc70fe8724ca0bee3dcd1a6c8b1f Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 9 Oct 2025 20:31:23 +0200 Subject: [PATCH 3/9] Update pages/cloudflare/perf.mdx Co-authored-by: khuezy --- pages/cloudflare/perf.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/cloudflare/perf.mdx b/pages/cloudflare/perf.mdx index 090b706..93c639f 100644 --- a/pages/cloudflare/perf.mdx +++ b/pages/cloudflare/perf.mdx @@ -10,7 +10,7 @@ You should regularly update to the latest available version of [`@opennextjs/clo ### Caching -Cacing can drastically improves the performance of your application by only fetching data / re-generating your pages when they change. +Caching can drastically improves the performance of your application by only fetching data / re-generating your pages when they change. To get the most out of caching, you should start by reading how Next.js implements caching. The ["Caching in Next.js" guide](https://nextjs.org/docs/app/guides/caching) is a good place to start. From 6dbd7092b00be50d585082b73208584794843c71 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 10 Oct 2025 08:39:37 +0200 Subject: [PATCH 4/9] Update pages/cloudflare/perf.mdx Co-authored-by: James Anderson --- pages/cloudflare/perf.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/cloudflare/perf.mdx b/pages/cloudflare/perf.mdx index 93c639f..a792d13 100644 --- a/pages/cloudflare/perf.mdx +++ b/pages/cloudflare/perf.mdx @@ -2,7 +2,7 @@ import { Callout } from "nextra/components"; ## Performance tips -They are a lot fo features and knobs you can tune to imrpve the performance of your app. This pages list some of the recommend settings. +They are a lot of features and knobs you can tune to improve the performance of your app. This page lists some of the recommend settings. ### General From 3ecb3e8457105e0d1b075de3e83dd70d5bc9fd0d Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 10 Oct 2025 08:39:44 +0200 Subject: [PATCH 5/9] Update pages/cloudflare/perf.mdx Co-authored-by: James Anderson --- pages/cloudflare/perf.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/cloudflare/perf.mdx b/pages/cloudflare/perf.mdx index a792d13..04e1b5e 100644 --- a/pages/cloudflare/perf.mdx +++ b/pages/cloudflare/perf.mdx @@ -43,9 +43,9 @@ You should add a `public/_headers` file to cache the static assets served by you See the [Cloudflare documentation](https://developers.cloudflare.com/workers/static-assets/headers/) for a detailed explanation of the default and the syntax. -### Troubleshoting performance +### Troubleshooting performance -You can profile the code to troubleshot performance issues. +You can profile the code to troubleshoot performance issues. #### Building unminified code From 007483b3f6d1cfd7206788ffc3344faa3fa1ce19 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 10 Oct 2025 08:39:51 +0200 Subject: [PATCH 6/9] Update pages/cloudflare/_meta.json Co-authored-by: James Anderson --- pages/cloudflare/_meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/cloudflare/_meta.json b/pages/cloudflare/_meta.json index d5204d4..c46d124 100644 --- a/pages/cloudflare/_meta.json +++ b/pages/cloudflare/_meta.json @@ -6,7 +6,7 @@ "caching": "", "howtos": "How-Tos", "examples": "", - "perf": "Perfomance Tips", + "perf": "Performance Tips", "community": "Community projects", "known-issues": "Known issues", "troubleshooting": "", From 7f46b7124ff962a17e452ddb67358cdf26057861 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 10 Oct 2025 08:49:35 +0200 Subject: [PATCH 7/9] fixup! multi-workers, minor edits --- pages/cloudflare/perf.mdx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pages/cloudflare/perf.mdx b/pages/cloudflare/perf.mdx index 04e1b5e..0f75e8a 100644 --- a/pages/cloudflare/perf.mdx +++ b/pages/cloudflare/perf.mdx @@ -18,6 +18,8 @@ You should then check [how to configure caching](/cloudflare/caching) for the Op #### [Incremental Cache](/cloudflare/caching#incremental-static-regeneration-isr) +The Incremental cache is the store containing all the cached data (i.e. pages, `fetch`, `unstable_cache`). + You should use the Workers Static Assets based cache if your site is SSG. It is the fastest available option. Note that as Workers Static Assets are read-only, this option can not be used with revalidation. When your app uses re-validation, use the R2 based store instead. We recommend the following settings to get the best perfomance: @@ -31,7 +33,9 @@ When your app uses re-validation, use the R2 based store instead. We recommend t #### [Tag Cache](/cloudflare/caching#tag-cache-for-on-demand-revalidation) -The Tag should be configured for App-Router based app using `revalidateTag` or `revalidatePath`. +The Tag Cache is not properly a cache. It only stores the timestamp at which tags have been revalidated. + +It should be configured for App-Router based app using `revalidateTag` or `revalidatePath`. The D1 based tag cache should only be used if your site receives low traffic. The Durable Object based tag cache is the recommend option for most sites. See [the reference guide](/cloudflare/caching#tag-cache-for-on-demand-revalidation) for the available configuration options. @@ -43,6 +47,10 @@ You should add a `public/_headers` file to cache the static assets served by you See the [Cloudflare documentation](https://developers.cloudflare.com/workers/static-assets/headers/) for a detailed explanation of the default and the syntax. +### [Multiple Workers](https://opennext.js.org/cloudflare/howtos/multi-worker) + +Deploying the middleware and the main server to distinct Workers can help with performance. As when a page can be retrieved from the cache, the main server can be fully bypassed. + ### Troubleshooting performance You can profile the code to troubleshoot performance issues. From 8cbde2a7fd4f86baa283428e06b80f56ef21043b Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 10 Oct 2025 09:22:27 +0200 Subject: [PATCH 8/9] Update pages/cloudflare/perf.mdx Co-authored-by: conico974 --- pages/cloudflare/perf.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/cloudflare/perf.mdx b/pages/cloudflare/perf.mdx index 0f75e8a..ca75809 100644 --- a/pages/cloudflare/perf.mdx +++ b/pages/cloudflare/perf.mdx @@ -29,7 +29,7 @@ When your app uses re-validation, use the R2 based store instead. We recommend t - you should not need to explicitly set `shouldLazilyUpdateOnCacheHit` nor `bypassTagCacheOnCacheHit` as they are set to the most performant mode by default - setup [automatic cache purge](/cloudflare/caching#automatic-cache-purge) -Using KV is not recommended as it is eventually consistent. +Using KV is not recommended as it is eventually consistent. It could cause stale data to be persisted indefinitely #### [Tag Cache](/cloudflare/caching#tag-cache-for-on-demand-revalidation) From 8772836ec8f92e69a73d1c12f3c7599491a56fe1 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 10 Oct 2025 09:32:22 +0200 Subject: [PATCH 9/9] fixup! lint --- pages/cloudflare/perf.mdx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pages/cloudflare/perf.mdx b/pages/cloudflare/perf.mdx index ca75809..9cf7430 100644 --- a/pages/cloudflare/perf.mdx +++ b/pages/cloudflare/perf.mdx @@ -29,7 +29,10 @@ When your app uses re-validation, use the R2 based store instead. We recommend t - you should not need to explicitly set `shouldLazilyUpdateOnCacheHit` nor `bypassTagCacheOnCacheHit` as they are set to the most performant mode by default - setup [automatic cache purge](/cloudflare/caching#automatic-cache-purge) -Using KV is not recommended as it is eventually consistent. It could cause stale data to be persisted indefinitely + + Using KV is not recommended as it is eventually consistent. It could cause stale data to be persisted + indefinitely + #### [Tag Cache](/cloudflare/caching#tag-cache-for-on-demand-revalidation)