diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 5d545f15a9f1b..681ce6226a8c2 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -1688,6 +1688,7 @@ async function renderToStream( let reactServerResult: null | ReactServerResult = null const setHeader = res.setHeader.bind(res) + const appendHeader = res.appendHeader.bind(res) try { if ( @@ -1857,7 +1858,7 @@ async function renderToStream( nonce: ctx.nonce, onHeaders: (headers: Headers) => { headers.forEach((value, key) => { - setHeader(key, value) + appendHeader(key, value) }) }, maxHeadersLength: renderOpts.reactMaxHeadersLength, @@ -2519,14 +2520,25 @@ async function prerenderToStream( | null | ReactServerPrerenderResult | ServerPrerenderStreamResult = null - const setHeader = (name: string, value: string | string[]) => { - res.setHeader(name, value) - + const setMetadataHeader = (name: string) => { metadata.headers ??= {} metadata.headers[name] = res.getHeader(name) - + } + const setHeader = (name: string, value: string | string[]) => { + res.setHeader(name, value) + setMetadataHeader(name) return res } + const appendHeader = (name: string, value: string | string[]) => { + if (Array.isArray(value)) { + value.forEach((item) => { + res.appendHeader(name, item) + }) + } else { + res.appendHeader(name, value) + } + setMetadataHeader(name) + } let prerenderStore: PrerenderStore | null = null @@ -2875,7 +2887,7 @@ async function prerenderToStream( }, onHeaders: (headers: Headers) => { headers.forEach((value, key) => { - setHeader(key, value) + appendHeader(key, value) }) }, maxHeadersLength: renderOpts.reactMaxHeadersLength, @@ -3493,7 +3505,7 @@ async function prerenderToStream( onError: htmlRendererErrorHandler, onHeaders: (headers: Headers) => { headers.forEach((value, key) => { - setHeader(key, value) + appendHeader(key, value) }) }, maxHeadersLength: renderOpts.reactMaxHeadersLength, diff --git a/test/e2e/app-dir/app-middleware/app-middleware.test.ts b/test/e2e/app-dir/app-middleware/app-middleware.test.ts index 7920e3efc942c..3407201a81f31 100644 --- a/test/e2e/app-dir/app-middleware/app-middleware.test.ts +++ b/test/e2e/app-dir/app-middleware/app-middleware.test.ts @@ -123,6 +123,13 @@ describe('app-dir with middleware', () => { }) }) + it('retains a link response header from the middleware', async () => { + const res = await next.fetch('/preloads') + expect(res.headers.get('link')).toContain( + '; rel="alternate"; hreflang="en"' + ) + }) + it('should be possible to modify cookies & read them in an RSC in a single request', async () => { const browser = await next.browser('/rsc-cookies') diff --git a/test/e2e/app-dir/app-middleware/app/preloads/page.js b/test/e2e/app-dir/app-middleware/app/preloads/page.js new file mode 100644 index 0000000000000..3cbe6210546ce --- /dev/null +++ b/test/e2e/app-dir/app-middleware/app/preloads/page.js @@ -0,0 +1,13 @@ +import Image from 'next/image' +import { Inter } from 'next/font/google' + +const inter = Inter({ subsets: ['latin'] }) + +export default function Page() { + return ( + <> + favicon +

Hello World

+ + ) +} diff --git a/test/e2e/app-dir/app-middleware/middleware.js b/test/e2e/app-dir/app-middleware/middleware.js index 0e4dd2ea8ad95..ccb4785577e25 100644 --- a/test/e2e/app-dir/app-middleware/middleware.js +++ b/test/e2e/app-dir/app-middleware/middleware.js @@ -69,6 +69,15 @@ export async function middleware(request) { return res } + if (request.nextUrl.pathname === '/preloads') { + const res = NextResponse.next({ + headers: { + link: '; rel="alternate"; hreflang="en"', + }, + }) + return res + } + return NextResponse.next({ request: { headers: headersFromRequest,