Skip to content

Commit 364db96

Browse files
authored
feat(next/image)!: deprecate and warn on images.domains config (#84625)
This was docs deprecated in Next.js 14 so we should begin warning in Next.js 16 when `images.domains` config is used. This will guide users to providing a strict `images.remotePatterns` config ensuring only expected images can be optimized.
1 parent 8c52bd6 commit 364db96

File tree

4 files changed

+35
-6
lines changed

4 files changed

+35
-6
lines changed

docs/01-app/03-api-reference/02-components/image.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -871,10 +871,12 @@ You can optionally configure `inline` to allow the browser to render the image w
871871

872872
#### `domains`
873873

874-
> **Warning**: Deprecated since Next.js 14 in favor of strict [`remotePatterns`](#remotepatterns) in order to protect your application from malicious users. Only use `domains` if you own all the content served from the domain.
874+
> **Warning**: Deprecated since Next.js 14 in favor of strict [`remotePatterns`](#remotepatterns) in order to protect your application from malicious users.
875875
876876
Similar to [`remotePatterns`](#remotepatterns), the `domains` configuration can be used to provide a list of allowed hostnames for external images. However, the `domains` configuration does not support wildcard pattern matching and it cannot restrict protocol, port, or pathname.
877877

878+
Since most remote image servers are shared between multiple tenants, it's safer to use `remotePatterns` to ensure only the intended images are optimized.
879+
878880
Below is an example of the `domains` property in the `next.config.js` file:
879881

880882
```js filename="next.config.js"

packages/next/src/server/config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ function checkDeprecations(
120120
silent
121121
)
122122

123+
if (userConfig.images?.domains?.length) {
124+
warnOptionHasBeenDeprecated(
125+
userConfig,
126+
'images.domains',
127+
`\`images.domains\` is deprecated in favor of \`images.remotePatterns\`. Please update ${configFileName} to protect your application from malicious users.`,
128+
silent
129+
)
130+
}
131+
123132
// i18n deprecation for App Router
124133
if (userConfig.i18n) {
125134
const hasAppDir = Boolean(findDir(dir, 'app'))

packages/next/src/shared/lib/image-config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ export const imageConfigDefault: ImageConfigComplete = {
133133
path: '/_next/image',
134134
loader: 'default',
135135
loaderFile: '',
136+
/**
137+
* @deprecated Use `remotePatterns` instead to protect your application from malicious users.
138+
*/
136139
domains: [],
137140
disableStaticImages: false,
138141
minimumCacheTTL: 14400, // 4 hours

test/integration/next-image-new/image-from-node-modules/test/index.test.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const appDir = join(__dirname, '../')
1313
let appPort
1414
let app
1515

16-
function runTests() {
16+
function runTests(getOutput: () => string) {
1717
it('should apply image config for node_modules', async () => {
1818
const browser = await webdriver(appPort, '/')
1919
const src = await browser
@@ -26,34 +26,49 @@ function runTests() {
2626
.getAttribute('srcset')
2727
expect(srcset).toMatch('1234')
2828
})
29+
30+
it('should warn when using images.domains config', async () => {
31+
expect(getOutput()).toContain(
32+
'`images.domains` is deprecated in favor of `images.remotePatterns`. Please update next.config.js to protect your application from malicious users.'
33+
)
34+
})
2935
}
3036

3137
describe('Image Component from node_modules prod mode', () => {
3238
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
3339
'production mode',
3440
() => {
41+
let output = ''
3542
beforeAll(async () => {
36-
await nextBuild(appDir)
43+
const result = await nextBuild(appDir, [], {
44+
stderr: true,
45+
stdout: true,
46+
})
47+
output = (result.stderr ?? '') + (result.stdout ?? '')
3748
appPort = await findPort()
3849
app = await nextStart(appDir, appPort)
3950
})
4051
afterAll(async () => {
4152
await killApp(app)
4253
})
4354

44-
runTests()
55+
runTests(() => output)
4556
}
4657
)
4758
})
4859

4960
describe('Image Component from node_modules development mode', () => {
61+
let output = ''
5062
beforeAll(async () => {
5163
appPort = await findPort()
52-
app = await launchApp(appDir, appPort)
64+
app = await launchApp(appDir, appPort, {
65+
onStderr: (msg) => (output += msg),
66+
onStdout: (msg) => (output += msg),
67+
})
5368
})
5469
afterAll(async () => {
5570
await killApp(app)
5671
})
5772

58-
runTests()
73+
runTests(() => output)
5974
})

0 commit comments

Comments
 (0)