Skip to content

fix(image): allow any quality 1-100 when images.qualities is unset - #2023

Merged
james-elicx merged 2 commits into
cloudflare:mainfrom
james-elicx:claude/hopeful-fermi-d9f042
Jun 15, 2026
Merged

fix(image): allow any quality 1-100 when images.qualities is unset#2023
james-elicx merged 2 commits into
cloudflare:mainfrom
james-elicx:claude/hopeful-fermi-d9f042

Conversation

@james-elicx

Copy link
Copy Markdown
Member

Problem

The image optimization endpoint returns 400 Bad Request for any quality other than 75. For example, the app-router-playground product card uses <Image quality={90}>, and the deployed endpoint fails:

GET /_next/image?url=%2Fshop%2Ftablet.png&w=750&q=90  ->  400 Bad Request
GET /_next/image?url=%2Fshop%2Ftablet.png&w=640&q=75  ->  200 OK

Only q=75 worked; q=50, q=90, q=100 all 400'd regardless of width.

Root cause

parseImageParams defaulted the allowed-quality list to DEFAULT_IMAGE_QUALITIES = [75] whenever images.qualities was not configured, and the build/runtime plumbing baked [75] into the deploy worker (__VINEXT_IMAGE_QUALITIES ?? "[75]").

Next.js only enforces a quality allowlist when images.qualities is explicitly set. When unset, it permits any integer quality from 1–100 — the allowlist block is skipped entirely:

// next/src/server/image-optimizer.ts — validateParams
const quality = parseInt(q, 10)
if (isNaN(quality) || quality < 1 || quality > 100) { /* reject */ }
if (qualities) {                       // only when configured
  if (isDev) qualities.push(BLUR_QUALITY)
  if (!qualities.includes(quality)) { /* reject */ }
}

Fix

  • parseImageParams / resolveDevImageRedirect take an optional allowedQualities and only enforce the allowlist when one is provided (still range-checks 1–100 and preserves the dev BLUR_QUALITY exception).
  • Removed the [75] default and the ?? DEFAULT_IMAGE_QUALITIES / ?? "[75]" fallbacks; the build now emits null when qualities is unset so the "allow all" semantics survive into the deploy worker.
  • Covers all call paths (app-router, pages-router, dev server, deploy worker) since they all route through the shared parseImageParams.

Tests

  • Regression test: any quality 1–100 passes when qualities is unset.
  • Allowlist is still enforced when qualities is configured.
  • Updated the prior test that asserted the buggy [75]-only behaviour.

Follow-up

A stacked PR will address the remaining validateParams parity gaps (recursive /_next/image guard, localPatterns, remotePatterns, minimumCacheTTL).

The image optimization endpoint returned 400 for any quality other than
75 (e.g. `<Image quality={90}>`) because vinext defaulted the allowed
quality list to `[75]` whenever `images.qualities` was not configured.

Next.js only enforces a quality allowlist when `images.qualities` is set;
when unset it permits any integer quality from 1-100. This aligns vinext
with that behaviour: `parseImageParams`/`resolveDevImageRedirect` now take
an optional allowlist and only restrict quality when one is provided, and
the build/runtime plumbing emits `null` (not `[75]`) when unset so the
"allow all" semantics survive into the deploy worker.

Repro: GET /_next/image?url=%2Fshop%2Ftablet.png&w=750&q=90 -> 400.

Fixes the app-router-playground image optimization failure.
@pkg-pr-new

pkg-pr-new Bot commented Jun 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2023
npm i https://pkg.pr.new/vinext@2023

commit: 2fb2e3a

With `images.qualities` unset, any quality 1-100 is now permitted (Next.js
parity), so q=70 is a normal quality rather than a rejected one. Pin the
blur-quality-exception tests to `qualities: [75]` so they still exercise the
dev-only exception, and add a case asserting q=70 is allowed in production
when qualities is unset.
@james-elicx
james-elicx marked this pull request as ready for review June 15, 2026 09:42
@james-elicx
james-elicx merged commit 62db980 into cloudflare:main Jun 15, 2026
44 checks passed
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.

1 participant