Security context
Medium· 6.2GHSA-g5qg-72qw-gw5v CVE-2025-57752CWE-524Published Aug 29, 2025

Next.js Affected by Cache Key Confusion for Image Optimization API Routes

Research this vulnerability

Research is free — Hunters explains how the bug works, the root-cause code pattern, how the fix addresses it, and how to test whether a target is affected, in chat. Investigate & write exploit is a paid run — the engine reads the advisory and fix commits, then builds and validates a working proof-of-concept exploit with reproduction steps.

Affected versions

0.9.9 → fixed in 14.2.3115.0.0 → fixed in 15.4.5

Details

A vulnerability in Next.js Image Optimization has been fixed in v15.4.5 and v14.2.31. When images returned from API routes vary based on request headers (such as `Cookie` or `Authorization`), these responses could be incorrectly cached and served to unauthorized users due to a cache key confusion bug. All users are encouraged to upgrade if they use API routes to serve images that depend on request headers and have image optimization enabled. More details at [Vercel Changelog](https://vercel.com/changelog/cve-2025-57752)

The fix

fix(next/image): add failing test

Steven· Jul 26, 2025, 03:06 PM+181fc3fb6baa1
test/integration/image-optimizer/app/pages/api/conditional-cookie.js+11 0
@@ -0,0 +1,11 @@
+const pixel =
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPj/HwADBwIAMCbHYQAAAABJRU5ErkJggg=='
+
+export default function handler(req, res) {
+ if (req.headers['cookie']) {
+ res.setHeader('content-type', 'image/png')
+ res.end(Buffer.from(pixel, 'base64'))
+ } else {
+ res.status(401).end('cookie was not found')
+ }
+}
test/integration/image-optimizer/test/util.ts+7 0
@@ -308,6 +308,13 @@ export function runTests(ctx: RunTestsCtx) {
expect(ctx.nextOutput).toContain(animatedWarnText)
})
+ it('should not forward cookie header', async () => {
+ const query = { w: ctx.w, q: 30, url: '/api/conditional-cookie' }
+ const opts = { headers: { accept: 'image/webp', cookie: '1' } }
+ const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts)
+ expect(res.status).toBe(400)
+ })
+
if (ctx.nextConfigImages?.dangerouslyAllowSVG) {
it('should maintain vector svg', async () => {
const query = { w: ctx.w, q: 90, url: '/test.svg' }
packages/next/src/server/image-optimizer.ts | 1 -
1 file changed, 1 deletion(-)
packages/next/src/server/image-optimizer.ts+0 1
@@ -634,7 +634,6 @@ export async function fetchInternalImage(
const mocked = createRequestResponseMocks({
url: href,
method: _req.method || 'GET',
- headers: _req.headers,
socket: _req.socket,
})

fix(next/image): fix image-optimizer.ts headers (#82114)

Steven· Jul 28, 2025, 01:06 PM+1816b12c60c61
packages/next/src/server/image-optimizer.ts+0 1
@@ -634,7 +634,6 @@ export async function fetchInternalImage(
const mocked = createRequestResponseMocks({
url: href,
method: _req.method || 'GET',
- headers: _req.headers,
socket: _req.socket,
})
test/integration/image-optimizer/app/pages/api/conditional-cookie.js+11 0
@@ -0,0 +1,11 @@
+const pixel =
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPj/HwADBwIAMCbHYQAAAABJRU5ErkJggg=='
+
+export default function handler(req, res) {
+ if (req.headers['cookie']) {
+ res.setHeader('content-type', 'image/png')
+ res.end(Buffer.from(pixel, 'base64'))
+ } else {
+ res.status(401).end('cookie was not found')
+ }
+}
test/integration/image-optimizer/test/util.ts+7 0
@@ -308,6 +308,13 @@ export function runTests(ctx: RunTestsCtx) {
expect(ctx.nextOutput).toContain(animatedWarnText)
})
+ it('should not forward cookie header', async () => {
+ const query = { w: ctx.w, q: 30, url: '/api/conditional-cookie' }
+ const opts = { headers: { accept: 'image/webp', cookie: '1' } }
+ const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts)
+ expect(res.status).toBe(400)
+ })
+
if (ctx.nextConfigImages?.dangerouslyAllowSVG) {
it('should maintain vector svg', async () => {
const query = { w: ctx.w, q: 90, url: '/test.svg' }

References