Skip to content

Commit 1a0f4d0

Browse files
authored
templates: fix broken images on Next.js 16 by using relative paths for local media (#16058)
Fixes #16051 Next.js 16 tightened image security so that absolute URLs resolving to private IPs are now blocked. This broke all media in the templates because `getMediaUrl` was prepending `getClientSideURL()` to build absolute URLs like `http://localhost:3000/api/media/file/image.webp`, which then got rejected during image optimization. This PR removes the `getClientSideURL()` prepend from `getMediaUrl` for local paths and adds a required `localPatterns` entry. External URLs from storage plugins are still returned as-is. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1213794221084158
1 parent 17266ab commit 1a0f4d0

11 files changed

Lines changed: 67 additions & 20 deletions

File tree

templates/_template/next.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ const __filename = fileURLToPath(import.meta.url)
77
const dirname = path.dirname(__filename)
88

99
const nextConfig: NextConfig = {
10+
images: {
11+
localPatterns: [
12+
{
13+
pathname: '/api/media/file/**',
14+
},
15+
],
16+
},
1017
webpack: (webpackConfig) => {
1118
webpackConfig.resolve.extensionAlias = {
1219
'.cjs': ['.cts', '.cjs'],

templates/blank/next.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ const __filename = fileURLToPath(import.meta.url)
77
const dirname = path.dirname(__filename)
88

99
const nextConfig: NextConfig = {
10+
images: {
11+
localPatterns: [
12+
{
13+
pathname: '/api/media/file/**',
14+
},
15+
],
16+
},
1017
webpack: (webpackConfig) => {
1118
webpackConfig.resolve.extensionAlias = {
1219
'.cjs': ['.cts', '.cjs'],

templates/ecommerce/next.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ const NEXT_PUBLIC_SERVER_URL = process.env.NEXT_PUBLIC_SERVER_URL || 'http://loc
1111

1212
const nextConfig: NextConfig = {
1313
images: {
14+
localPatterns: [
15+
{
16+
pathname: '/api/media/file/**',
17+
},
18+
],
1419
qualities: [90, 100],
1520
remotePatterns: [
1621
...[NEXT_PUBLIC_SERVER_URL /* 'https://example.com' */].map((item) => {

templates/website/next.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ const NEXT_PUBLIC_SERVER_URL = process.env.VERCEL_PROJECT_PRODUCTION_URL
1313

1414
const nextConfig: NextConfig = {
1515
images: {
16+
localPatterns: [
17+
{
18+
pathname: '/api/media/file/**',
19+
},
20+
],
1621
qualities: [100],
1722
remotePatterns: [
1823
...[NEXT_PUBLIC_SERVER_URL /* 'https://example.com' */].map((item) => {
Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { getClientSideURL } from '@/utilities/getURL'
2-
31
/**
42
* Processes media resource URL to ensure proper formatting
53
* @param url The original URL from the resource
64
* @param cacheTag Optional cache tag to append to the URL
75
* @returns Properly formatted URL with cache tag if provided
6+
*
7+
* Local paths (e.g. `/api/media/file/image.webp`) are kept relative so
8+
* Next.js image optimization treats them as local rather than fetching
9+
* through `remotePatterns`, which blocks private IPs since Next.js 16.
810
*/
911
export const getMediaUrl = (url: string | null | undefined, cacheTag?: string | null): string => {
1012
if (!url) return ''
@@ -13,12 +15,5 @@ export const getMediaUrl = (url: string | null | undefined, cacheTag?: string |
1315
cacheTag = encodeURIComponent(cacheTag)
1416
}
1517

16-
// Check if URL already has http/https protocol
17-
if (url.startsWith('http://') || url.startsWith('https://')) {
18-
return cacheTag ? `${url}?${cacheTag}` : url
19-
}
20-
21-
// Otherwise prepend client-side URL
22-
const baseUrl = getClientSideURL()
23-
return cacheTag ? `${baseUrl}${url}?${cacheTag}` : `${baseUrl}${url}`
18+
return cacheTag ? `${url}?${cacheTag}` : url
2419
}

templates/with-cloudflare-d1/next.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ import { withPayload } from '@payloadcms/next/withPayload'
22

33
/** @type {import('next').NextConfig} */
44
const nextConfig = {
5+
images: {
6+
localPatterns: [
7+
{
8+
pathname: '/api/media/file/**',
9+
},
10+
],
11+
},
512
// Packages with Cloudflare Workers (workerd) specific code
613
// Read more: https://opennext.js.org/cloudflare/howtos/workerd
714
serverExternalPackages: ['jose', 'pg-cloudflare'],

templates/with-postgres/next.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ const __filename = fileURLToPath(import.meta.url)
77
const dirname = path.dirname(__filename)
88

99
const nextConfig: NextConfig = {
10+
images: {
11+
localPatterns: [
12+
{
13+
pathname: '/api/media/file/**',
14+
},
15+
],
16+
},
1017
webpack: (webpackConfig) => {
1118
webpackConfig.resolve.extensionAlias = {
1219
'.cjs': ['.cts', '.cjs'],

templates/with-vercel-mongodb/next.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ const __filename = fileURLToPath(import.meta.url)
77
const dirname = path.dirname(__filename)
88

99
const nextConfig: NextConfig = {
10+
images: {
11+
localPatterns: [
12+
{
13+
pathname: '/api/media/file/**',
14+
},
15+
],
16+
},
1017
webpack: (webpackConfig) => {
1118
webpackConfig.resolve.extensionAlias = {
1219
'.cjs': ['.cts', '.cjs'],

templates/with-vercel-postgres/next.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ const __filename = fileURLToPath(import.meta.url)
77
const dirname = path.dirname(__filename)
88

99
const nextConfig: NextConfig = {
10+
images: {
11+
localPatterns: [
12+
{
13+
pathname: '/api/media/file/**',
14+
},
15+
],
16+
},
1017
webpack: (webpackConfig) => {
1118
webpackConfig.resolve.extensionAlias = {
1219
'.cjs': ['.cts', '.cjs'],

templates/with-vercel-website/next.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ const NEXT_PUBLIC_SERVER_URL = process.env.VERCEL_PROJECT_PRODUCTION_URL
1313

1414
const nextConfig: NextConfig = {
1515
images: {
16+
localPatterns: [
17+
{
18+
pathname: '/api/media/file/**',
19+
},
20+
],
1621
qualities: [100],
1722
remotePatterns: [
1823
...[NEXT_PUBLIC_SERVER_URL /* 'https://example.com' */].map((item) => {

0 commit comments

Comments
 (0)