Files
Luke Sandberg 806fbd4367 fix(next/image): preserve image response after optimization (#96681)
Fork PR #96621 by @ceolinwill, re-opened as a branch PR so the "when
deployed" CI job can run — it requires Vercel deployment secrets that
GitHub does not expose to pull requests from forks, so it can never pass
on the original.

**The commit is unchanged and still authored by @ceolinwill.** Please
credit them; #96621 should be closed in favor of this one.

### What?

`getSharp()` calls `_sharp.block({ operation: ['VipsForeignLoad'] })` to
block every image loader, then unblocks a specific allowlist — and that
allowlist omitted `VipsForeignLoadSvg`. Because `_sharp` is a
module-level singleton, the block is process-wide and permanent: the
first `/_next/image` request in a process permanently disables Sharp's
SVG loader.

`ImageResponse` (`next/og`) rasterizes via resvg and then hands SVG to
Sharp, so once that loader is blocked it fails. The symptom is worse
than a bad image — the render throws `Input buffer contains unsupported
image format`, which surfaces as a **socket hang up / crashed response**
on any `ImageResponse` route requested after an uncached image
optimization in the same process.

Introduced by #96301, which aligned the Sharp allowlist with
`detectContentType()` but missed SVG.

Fixes #96612

### How?

Adds `'VipsForeignLoadSvg'` to the unblock list.

This does not weaken SVG protections for user-supplied images.
`detectContentType()` already returns SVG, and untrusted SVG is gated
separately by `dangerouslyAllowSVG`, which throws a 400 in
`imageOptimizer()` before Sharp is ever invoked. Unblocking the loader
only restores Sharp's ability to process SVG that Next.js itself
generates.

### Tests

The existing `og-api` test is extended to reproduce the exact
same-process ordering: render `/og-node`, perform an uncached
`/_next/image` optimization (asserting `x-nextjs-cache: MISS`, with a
random URL so it can't be served from cache), then render `/og-node`
again.

Verified locally that this fails without the one-line fix (`socket hang
up`, caused by `Input buffer contains unsupported image format`) and
passes with it.

No regressions in the SVG security coverage —
`test/e2e/image-optimizer/dangerously-allow-svg.test.ts` passes 94/94
including all `maintain vector svg` / blur-svg cases, and
`test/e2e/image-optimizer/image-optimizer.test.ts` plus the `og-*`
suites are green.

Co-authored-by: ceolinwill <4393133+ceolinwill@users.noreply.github.com>
2026-08-05 15:13:24 +00:00
..