Skip to content

Commit

Permalink
Allow external image urls with _next/image pathname to be rendered vi…
Browse files Browse the repository at this point in the history
…a Image component (#69586)

### What?
Fixes #69456 which describes an issue with `next/image` component, where
an external image containing `_next/image` in the URL fails with the
error `"url" parameter cannot be recursive`.

### Why?
The `next/image` component should be able to render images from
configured hostnames without doing the recursive check.

### How?
Before checking for the existence of `_next/image` substring in the
image's path, we can simply check if the image is an external URL that
comes from a preconfigured host in the `next.config.js` file.

Example:
<img width="1207" alt="image"
src="https://github.com/user-attachments/assets/8d5c99cf-f050-40a9-b5a4-ba961b83f037">

---------

Co-authored-by: Steven <[email protected]>
  • Loading branch information
2 people authored and lubieowoce committed Sep 4, 2024
1 parent d09b769 commit 55e4ef2
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 25 deletions.
19 changes: 9 additions & 10 deletions packages/next/src/server/image-optimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,21 +198,20 @@ export class ImageOptimizerCache {
}
}

const parsedUrl = parseUrl(url)
if (parsedUrl) {
const decodedPathname = decodeURIComponent(parsedUrl.pathname)
if (/\/_next\/image($|\/)/.test(decodedPathname)) {
return {
errorMessage: '"url" parameter cannot be recursive',
}
}
}

let isAbsolute: boolean

if (url.startsWith('/')) {
href = url
isAbsolute = false
if (
/\/_next\/image($|\/)/.test(
decodeURIComponent(parseUrl(url)?.pathname ?? '')
)
) {
return {
errorMessage: '"url" parameter cannot be recursive',
}
}
} else {
let hrefParsed: URL

Expand Down
35 changes: 20 additions & 15 deletions test/integration/image-optimizer/test/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
fetchViaHTTP,
File,
findPort,
getFetchUrl,
killApp,
launchApp,
nextBuild,
Expand Down Expand Up @@ -899,22 +898,28 @@ export function runTests(ctx) {
expect(await res.text()).toBe(`"url" parameter is invalid`)
})

it('should fail with absolute next image url', async () => {
const fullUrl = getFetchUrl(
ctx.appPort,
'/_next/image?url=test.pngw=1&q=1'
)
const query = { url: fullUrl, w: ctx.w, q: 1 }
const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, {})
expect(res.status).toBe(400)
expect(await res.text()).toBe(`"url" parameter cannot be recursive`)
})
if (domains.length > 0) {
it('should pass with absolute next image url', async () => {
const fullUrl =
'https://image-optimization-test.vercel.app/_next/image?url=%2Ffrog.jpg&w=1024&q=75'
const query = { url: fullUrl, w: ctx.w, q: 1 }
const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, {})
expect(res.status).toBe(200)
await expectWidth(res, ctx.w)
})
} else {
it('should fail with absolute next image url', async () => {
const fullUrl =
'https://image-optimization-test.vercel.app/_next/image?url=%2Ffrog.jpg&w=1024&q=75'
const query = { url: fullUrl, w: ctx.w, q: 1 }
const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, {})
expect(res.status).toBe(400)
expect(await res.text()).toBe(`"url" parameter is not allowed`)
})
}

it('should fail with relative image url with assetPrefix', async () => {
const fullUrl = getFetchUrl(
ctx.appPort,
`/assets/_next/image?url=test.pngw=1&q=1`
)
const fullUrl = '/assets/_next/image?url=%2Ftest.png&w=128&q=75'
const query = { url: fullUrl, w: ctx.w, q: 1 }
const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, {})
expect(res.status).toBe(400)
Expand Down

0 comments on commit 55e4ef2

Please sign in to comment.