Skip to content

Ensure setAssetPrefix updates config instance #82160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/next/src/server/base-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1652,7 +1652,7 @@ export default abstract class Server<
): Promise<void>

public setAssetPrefix(prefix?: string): void {
this.renderOpts.assetPrefix = prefix ? prefix.replace(/\/$/, '') : ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're still using renderOpts.assetPrefix here, do we still need to update the renderOpts.assetPrefix as well?

publicPath: `${renderOpts.assetPrefix}/_next/`,

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.nextConfig.assetPrefix = prefix ? prefix.replace(/\/$/, '') : ''
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setAssetPrefix method now sets nextConfig.assetPrefix instead of renderOpts.assetPrefix, but other parts of the codebase still expect renderOpts.assetPrefix to be available, causing asset prefix to not work correctly.

View Details

Analysis

The change on line 1655 modified setAssetPrefix to update this.nextConfig.assetPrefix instead of this.renderOpts.assetPrefix. However, there are still two locations in the codebase that depend on renderOpts.assetPrefix:

  1. packages/next/src/server/async-storage/work-store.ts line 129: assetPrefix: renderOpts?.assetPrefix || '',
  2. packages/next/src/server/post-process.ts line 45: publicPath: \${renderOpts.assetPrefix}/_next/`,`

Both RenderOptsPartial interfaces in render.tsx and app-render/types.ts define assetPrefix?: string as an expected property. When setAssetPrefix is called during server initialization, the asset prefix will be stored in nextConfig but the code expecting it in renderOpts will receive undefined, potentially causing incorrect asset URLs or broken CSS optimization.


Recommendation

The setAssetPrefix method should update both locations to maintain consistency. Change line 1655 to:

public setAssetPrefix(prefix?: string): void {
  const assetPrefix = prefix ? prefix.replace(/\/$/, '') : ''
  this.nextConfig.assetPrefix = assetPrefix
  this.renderOpts.assetPrefix = assetPrefix
}

Alternatively, if the intention is to only store it in nextConfig, then the code in work-store.ts and post-process.ts should be updated to read from nextConfig.assetPrefix instead of renderOpts.assetPrefix.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above referenced renderOpts.assetPrefix usage is derived from the new handlers interface which is not from the base-server renderOpts instance. This will be cleared up while deleting base-server code.

}

protected prepared: boolean = false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { nextTestSetup } from 'e2e-utils'

describe('app-dir absolute assetPrefix', () => {
const { next } = nextTestSetup({
files: __dirname,
nextConfig: {
assetPrefix: 'https://example.vercel.sh/',
},
})

it('bundles should return 200 on served assetPrefix', async () => {
const $ = await next.render$('/')

let bundles = []
for (const script of $('script').toArray()) {
const { src } = script.attribs
if (src?.includes('https://example.vercel.sh/_next/static')) {
bundles.push(src)
}
}

expect(bundles.length).toBeGreaterThan(0)

for (const src of bundles) {
// Remove hostname to check if pathname is still used for serving the bundles
const bundlePathWithoutHost = decodeURI(new URL(src).pathname)
const { status } = await next.fetch(bundlePathWithoutHost)

expect(status).toBe(200)
}
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { nextTestSetup } from 'e2e-utils'
describe('app-dir absolute assetPrefix', () => {
const { next } = nextTestSetup({
files: __dirname,
nextConfig: {
assetPrefix: 'https://example.vercel.sh/custom-asset-prefix',
},
})

it('bundles should return 200 on served assetPrefix', async () => {
Expand Down
3 changes: 0 additions & 3 deletions test/e2e/app-dir/asset-prefix-absolute/next.config.js

This file was deleted.

Loading