Skip to content

Conversation

@pilcrowonpaper
Copy link

No description provided.

@changeset-bot
Copy link

changeset-bot bot commented Nov 20, 2025

⚠️ No Changeset found

Latest commit: afed612

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@sommeeeer
Copy link
Collaborator

Hello @pilcrowonpaper, good to see you here :) looks good on first glance, I'll do another review later!

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 20, 2025

Open in StackBlitz

npm i https://pkg.pr.new/@opennextjs/cloudflare@999

commit: 0833f86

} else {
const cacheControlHeader = imageResponse.headers.get("Cache-Control");
if (cacheControlHeader !== null) {
immutable = cacheControlHeader.includes("immutable");
Copy link
Author

Choose a reason for hiding this comment

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

Next.js does something more complicated and bases the optimized response on the upstream Cache-Control header. I didn't want to do anything complex for now so it just checks if the upstream image is a static content

@pilcrowonpaper
Copy link
Author

It looks like the Playwright tests are failing because the test cases don't include the required w and q parameters.

const __IMAGES_CONTENT_DISPOSITION__ = JSON.stringify(
imagesManifest?.images?.contentDispositionType ?? "attachment"
);
const __IMAGES_MAX_REDIRECTS__ = JSON.stringify(imagesManifest?.images?.maximumRedirects ?? 3);
Copy link
Author

Choose a reason for hiding this comment

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

This was changed to 3 from unlimited in Next.js 16

Copy link
Contributor

Choose a reason for hiding this comment

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

We could use unlimited then (and maybe add your comment as a code comment on this file/line)

Copy link
Contributor

Choose a reason for hiding this comment

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

What's the status here?


const defaultDeviceSizes = [640, 750, 828, 1080, 1200, 1920, 2048, 3840];
const defaultImageSizes = [32, 48, 64, 96, 128, 256, 384];
const defaultQualities = [75];
Copy link
Author

Choose a reason for hiding this comment

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

Before Next.js 16, the default behavior was to allow values from 1-100

Copy link
Contributor

Choose a reason for hiding this comment

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

What's the status here?

}

const defaultDeviceSizes = [640, 750, 828, 1080, 1200, 1920, 2048, 3840];
const defaultImageSizes = [32, 48, 64, 96, 128, 256, 384];
Copy link
Author

Choose a reason for hiding this comment

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

Before Next.js 16, 16 was also included

Copy link
Contributor

Choose a reason for hiding this comment

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

I would use Next 16 (and add comments on the lines).

nit: maybe move those constants up (i.e. before they are used)

Copy link
Contributor

Choose a reason for hiding this comment

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

What's the status here?

async function fetchImage(url: string, count: number): Promise<FetchImageResult> {
let response: Response;
try {
response = await fetch(url, {
Copy link
Author

Choose a reason for hiding this comment

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

I haven't added support for dangerouslyAllowLocalIP, which checks if a domain resolves to a private IP.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please add a note in the JSDoc?

@pilcrowonpaper
Copy link
Author

@vicb
Should the behavior match Next.js 15 or 16 in general? Or should we check the version and change the behavior based on it? Not sure what the policy is on Next.js versions

: {};

const __IMAGES_REMOTE_PATTERNS__ = JSON.stringify(imagesManifest?.images?.remotePatterns ?? []);
const __IMAGES_LOCAL_PATTERNS_DEFINED__ = JSON.stringify(
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this needed given that __IMAGES_LOCAL_PATTERNS__ default to an empty list?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, Next.js treats undefined and an empty list differently

Copy link
Contributor

Choose a reason for hiding this comment

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

How so?
If the behavior is different then we could pass the value differently to avoid having to defines.

Copy link
Author

Choose a reason for hiding this comment

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

I updated the variable name so we don't have an awkward "is defined" variable

return new Response('"url" parameter is valid but upstream response is invalid', {
status: 400,
});
type ParseImageRequestURLResult = ParseImageRequestURLSuccessResult | ErrorResult;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: move the type above the function where they are used

const WEBP = "image/webp";
const PNG = "image/png";
const JPEG = "image/jpeg";
const JXL = "image/jxl";
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: why have those been removed? (IIRC it comes from Next)

Copy link
Author

Choose a reason for hiding this comment

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

I've removed formats that aren't supported by Cloudflare:
https://developers.cloudflare.com/images/transform-images/

Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if the image binding is not used and we try to access one of this removed type?

Copy link
Author

Choose a reason for hiding this comment

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

That part I was unsure. I personally think we should only serve images that are supported by the optimization process, even when the binding isn't defined. It'd be weird if your images stopped being served when you turn on image optimization by defining the binding. It will be a breaking change though

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's not do this in this PR to keep the current behavior.
We can decide to changer at a later point.

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.

Awesome @pilcrowonpaper !

I have done a frist round of review.

Could you please add JSDoc comments to added functions.
Maybe there could be separate functions to i.e. validate each parameter?

@vicb vicb marked this pull request as ready for review November 25, 2025 13:19
try {
response = await fetch(url, {
signal: AbortSignal.timeout(timeout),
redirect: "manual",
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: only use "manual" when redirect is not max?

Copy link
Author

Choose a reason for hiding this comment

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

Like passing error?

Copy link
Contributor

Choose a reason for hiding this comment

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

You commented that Next 16 do not limit the number of redirects.
In that case we should not do manual redirects but just follow redirects.
Do that make sense?

Copy link
Author

Choose a reason for hiding this comment

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

No, I meant the opposite. Next.js 16 does limit redirects, while it didn't in Next.js 15

Comment on lines +259 to +264
response.headers.set("Vary", "Accept");
response.headers.set("Content-Type", contentType);
response.headers.set("Content-Disposition", __IMAGES_CONTENT_DISPOSITION__);
response.headers.set("Content-Security-Policy", __IMAGES_CONTENT_SECURITY_POLICY__);
if (imageResponseFlags.immutable) {
response.headers.set("Cache-Control", "public, max-age=315360000, immutable");
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: passing this in the Response ctor (options.headers) might be less verbose

Copy link
Contributor

Choose a reason for hiding this comment

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

??

@pilcrowonpaper
Copy link
Author

It looks like we can't add tests for the q parameter and some behaviors of the w parameter because Cloudflare image transformation doesn't support fit and quality locally.

: {};

const __IMAGES_REMOTE_PATTERNS__ = JSON.stringify(imagesManifest?.images?.remotePatterns ?? []);
const __IMAGES_ALLOW_ALL_LOCAL_PATHS__ = JSON.stringify(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we completely remove that?

i.e. set __IMAGES_LOCAL_PATTERNS__ to [{pathname: '**'}] when the local pattern is undefined?

Copy link
Author

Choose a reason for hiding this comment

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

Agree, my bad

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.

3 participants