Skip to content

Conversation

@dario-piotrowicz
Copy link
Member

Description

This PR adds various open-next improvements to the site (mainly for better performance)

The changes are:

  • bumped @opennextjs/cloudflare to the latest version (1.11.0)
  • replaced the kv incremental cache with the more performance r2 + regional cache implementation
  • Added a DurableObject queue that was missing for time-based revalidation
  • Enabled the open-next cache interception
  • Added a custom image loader for the Cloudflare deployment

Validation

I've validated these changes locally by running pnpm cloudflare:preview

Check List

  • I have read the Contributing Guidelines and made commit messages that follow the guideline.
  • I have run pnpm format to ensure the code follows the style guide.
  • I have run pnpm test to check if all tests are passing.
  • I have run pnpm build to check if the website builds without errors.
  • I've covered new added functionality with unit tests if necessary.

Copilot AI review requested due to automatic review settings October 31, 2025 18:34
@dario-piotrowicz dario-piotrowicz requested review from a team as code owners October 31, 2025 18:34
@vercel
Copy link

vercel bot commented Oct 31, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
nodejs-org Ready Ready Preview Nov 4, 2025 4:05pm

@dario-piotrowicz
Copy link
Member Author

@vicb it would be great if you could have a look and validate the open-next changes if you get a chance 🙂

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR upgrades the @opennextjs/cloudflare package from version 1.6.4 to 1.11.0 and implements enhanced caching and image optimization features for the Cloudflare deployment.

Key changes:

  • Upgraded @opennextjs/cloudflare to version 1.11.0, which includes updated dependencies like @opennextjs/aws and new packages (rclone.js, @types/rclone.js, adm-zip)
  • Migrated from KV-based caching to R2-based incremental caching with regional cache support and Durable Objects queue
  • Added custom Cloudflare image loader to leverage Cloudflare's image transformation service

Reviewed Changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pnpm-lock.yaml Updated package versions including @opennextjs/cloudflare (1.6.4→1.11.0), @opennextjs/aws (3.7.4→3.8.5), and added new dependencies (rclone.js, @types/rclone.js, adm-zip)
apps/site/package.json Upgraded @opennextjs/cloudflare dependency specification to ^1.11.0
apps/site/wrangler.jsonc Replaced KV namespace with R2 bucket configuration and added Durable Objects bindings for queue handling
apps/site/open-next.config.ts Updated configuration to use R2-based incremental cache with regional caching, DO queue, and cache interception
apps/site/next.config.mjs Added conditional image loader configuration to use Cloudflare's custom image loader when deploying to Cloudflare
apps/site/cloudflare-image-loader.ts New file implementing custom image loader for Cloudflare's image transformation service
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link

codecov bot commented Oct 31, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.76%. Comparing base (9fbb9f6) to head (ecc8a53).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8304      +/-   ##
==========================================
+ Coverage   76.72%   76.76%   +0.03%     
==========================================
  Files         118      118              
  Lines        9805     9816      +11     
  Branches      335      335              
==========================================
+ Hits         7523     7535      +12     
+ Misses       2280     2279       -1     
  Partials        2        2              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@dario-piotrowicz dario-piotrowicz force-pushed the dario/open-next-improvements branch from 93cdecb to 28ccc13 Compare October 31, 2025 18:40
@avivkeller avivkeller changed the title Apply various open-next improvements chore(open-next): apply various improvements Oct 31, 2025
@avivkeller avivkeller added the github_actions:pull-request Trigger Pull Request Checks label Oct 31, 2025
@github-actions github-actions bot removed the github_actions:pull-request Trigger Pull Request Checks label Oct 31, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Oct 31, 2025

Lighthouse Results

URL Performance Accessibility Best Practices SEO Report
/en 🟠 78 🟠 88 🟢 100 🟢 100 🔗
/en/about 🟢 100 🟢 93 🟢 100 🟠 88 🔗
/en/about/previous-releases 🟢 99 🟢 93 🟢 100 🟢 100 🔗
/en/download 🟢 97 🟢 96 🟢 100 🟢 100 🔗
/en/download/archive/current 🟢 100 🟢 100 🟢 100 🟢 100 🔗
/en/blog 🟢 100 🟢 100 🟢 96 🟢 100 🔗

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this go in a cloudflare/ subdirectory (cloudflare/images.ts or something)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of me really does not like this function. something about how it's designed 😅

I bert this can be simplified/cleaned up. + I'd rather not use the frigging process.env.NODE_ENV here and actually use the constants we have defined on next.constants. Please also document what this function is doing, why it works and Cloudflare external links. We need to know this exists and howit works.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is taken straight from the open-next documentation: https://opennext.js.org/cloudflare/howtos/image#use-a-custom-loader

I think it might be beneficial (since there are no like custom requirements or anything like that) to keep it as close as possible as the version in the open-next docs? That way if anything on the open-next site changes an updated loader version can just be copy-pasted here instead or having a more bespoke version that we need to more directly maintain? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this go in a cloudflare/ subdirectory (cloudflare/images.ts or something)?

Moved 👍

Comment on lines +1 to +22
import type { ImageLoaderProps } from 'next/image';

const normalizeSrc = (src: string) => {
return src.startsWith('/') ? src.slice(1) : src;
};

export default function cloudflareLoader({
src,
width,
quality,
}: ImageLoaderProps) {
if (process.env.NODE_ENV === 'development') {
// Serve the original image when using `next dev`
return src;
}
const params = [`width=${width}`];
if (quality) {
params.push(`quality=${quality}`);
}
const paramsString = params.join(',');
return `/cdn-cgi/image/${paramsString}/${normalizeSrc(src)}`;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few notes with this implementation:

  1. is normalizeSrc needed? Won't the leading / be normalized by the browser? If not, shouldn't there not be a slash / always be a slash passed here? This should only load images, inconsistencies should be resolved at their root, not here?

  2. I think this can be simplified to:

Suggested change
import type { ImageLoaderProps } from 'next/image';
const normalizeSrc = (src: string) => {
return src.startsWith('/') ? src.slice(1) : src;
};
export default function cloudflareLoader({
src,
width,
quality,
}: ImageLoaderProps) {
if (process.env.NODE_ENV === 'development') {
// Serve the original image when using `next dev`
return src;
}
const params = [`width=${width}`];
if (quality) {
params.push(`quality=${quality}`);
}
const paramsString = params.join(',');
return `/cdn-cgi/image/${paramsString}/${normalizeSrc(src)}`;
}
import type { ImageLoaderProps } from 'next/image';
// This can also be a `.replace(...)`?
const normalizeSrc = (src: string) => src.startsWith('/') ? src : `/${src}`;
// For tree-shaking, do this outside
export default process.env.NODE_ENV === 'development' ?
({ src }) => src :
({
src,
width,
quality,
}: ImageLoaderProps) => `/cdn-cgi/image/width=${width}${quality ? `quality=${quality}` : ''}${normalizeSrc(src)}`;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to go with your version, however as I mentioned there might be benefits in keeping this in sync with the open-next documentation? 🤔

(PS: if you want you can also try to upstream these changes in the open-next docs (cc. @vicb))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo, the initial implementation is a lot more readable than this (though normalizeSrc may be better as a just a .replace but I'm either or on it)

},
],
},
images:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a follow-up, I think we should make an apps/site/vercel folder and apps/site/cloudflare folder (well, it'll already exist, see my other comment) for the specific logic. Then, the config file itself will be simple, and call things like getCloudflareImageConfiguration() and getVercelImageConfiguration() or something?

wdyt @nodejs/nodejs-website

// adapter should set it itself when it invokes the Next.js build
// process, once it does that remove the manual `OPEN_NEXT_CLOUDFLARE`
// definition in the package.json script.
process.env.OPEN_NEXT_CLOUDFLARE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use direct process.env? Thanks! We use next.constants.mjs for all relevant constants.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also both these could be extracted into their own constants instead of a big ternary, easier to read.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use direct process.env? Thanks! We use next.constants.mjs for all relevant constants.

Done 👍

Also both these could be extracted into their own constants instead of a big ternary, easier to read.

I hope this is what you had in mind: 302ba1b


const cloudflareConfig = defineCloudflareConfig({ incrementalCache });
const cloudflareConfig = defineCloudflareConfig({
incrementalCache: withRegionalCache(r2IncrementalCache, {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add inline comments reference @see to relevant docs? And also inline comment what these settings do? 🙇

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description mentions

replaced the kv incremental cache with the more performance r2 + regional cache implementation

The actual issue with KV is that it is eventually consistent which might lead to unexpected behavior. This is why we recommend R2 instead - R2 is strongly consistent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you misunderstood, I'm asking for comments to be added on the code, so that it stays there for reference :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got that, I added the comment mostly for Dario to update the PR description and add comments here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ovflowd I've added an inline comment 65b4b4b

please let me know if this works for you 🙏

Copy link
Contributor

@vicb vicb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good to me when comments are addressed, thanks Dario!

A few notes:

  • Cloudflare images need to be enabled/configured for the account
  • R2 batch upload had been implemented recently to speed up cache asset upload. There are a few environment variables to set for this to work. I'll write the doc for it this week. Before that R2 upload could be slow.

@ovflowd
Copy link
Member

ovflowd commented Nov 3, 2025

Thanks Victor for the updates. Will keep an eye on your docs. Also, where can we enabled Cloudflare Images?

@dario-piotrowicz
Copy link
Member Author

where can we enabled Cloudflare Images?

https://developers.cloudflare.com/images/get-started/#enable-transformations-on-your-zone

I already did that on the testing/staging account

@vicb
Copy link
Contributor

vicb commented Nov 3, 2025

Thanks Victor for the updates. Will keep an eye on your docs. Also, where can we enabled Cloudflare Images?

opennextjs/docs#189 about R2 batching was just merged to the docs.

The 3 environments variables should be added the build time time env vars (provided you use Cloudflare CD).

@dario-piotrowicz dario-piotrowicz force-pushed the dario/open-next-improvements branch from 28ccc13 to 65b4b4b Compare November 4, 2025 12:43
Comment on lines +70 to +71
R2_ACCESS_KEY_ID: ${{ env.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added these two to the repository, I've created a token specific for the account that only has access to the site's incremental cache r2 bucket, I hope this works for you, if it doesn't I'm happy to change things 🙂

@dario-piotrowicz
Copy link
Member Author

The 3 environments variables should be added the build time time env vars (provided you use Cloudflare CD).

Added 🫡

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants