Skip to content

Commit 224ca18

Browse files
ztannerijjk
andauthored
remove unnecessary internal middleware header from response (#73482)
x-middleware-set-cookie is an internal header used by the middleware handler and doesn't need to be forwarded onto the response. this also adds handling to filter out internal request headers as they aren't intended to be used externally. --------- Co-authored-by: JJ Kasper <[email protected]>
1 parent 22daa8e commit 224ca18

File tree

11 files changed

+79
-1
lines changed

11 files changed

+79
-1
lines changed

packages/next/src/server/lib/router-server.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
import { normalizedAssetPrefix } from '../../shared/lib/normalized-asset-prefix'
4848
import { NEXT_PATCH_SYMBOL } from './patch-fetch'
4949
import type { ServerInitResult } from './render-server'
50+
import { filterInternalHeaders } from './server-ipc/utils'
5051

5152
const debug = setupDebug('next:router-server:main')
5253
const isNextFont = (pathname: string | null) =>
@@ -164,6 +165,11 @@ export async function initialize(opts: {
164165
require('./render-server') as typeof import('./render-server')
165166

166167
const requestHandlerImpl: WorkerRequestHandler = async (req, res) => {
168+
// internal headers should not be honored by the request handler
169+
if (!process.env.NEXT_PRIVATE_TEST_HEADERS) {
170+
filterInternalHeaders(req.headers)
171+
}
172+
167173
if (
168174
!opts.minimalMode &&
169175
config.i18n &&

packages/next/src/server/lib/router-utils/resolve-routes.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,14 @@ export function getResolveRoutes(
585585
) {
586586
continue
587587
}
588+
589+
// for set-cookie, the header shouldn't be added to the response
590+
// as it's only needed for the request to the middleware function.
591+
if (key === 'x-middleware-set-cookie') {
592+
req.headers[key] = value
593+
continue
594+
}
595+
588596
if (value) {
589597
resHeaders[key] = value
590598
req.headers[key] = value

packages/next/src/server/lib/server-ipc/utils.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,26 @@ export const filterReqHeaders = (
3636
}
3737
return headers as Record<string, undefined | string | string[]>
3838
}
39+
40+
// These are headers that are only used internally and should
41+
// not be honored from the external request
42+
const INTERNAL_HEADERS = [
43+
'x-middleware-rewrite',
44+
'x-middleware-redirect',
45+
'x-middleware-set-cookie',
46+
'x-middleware-skip',
47+
'x-middleware-override-headers',
48+
'x-middleware-next',
49+
'x-now-route-matches',
50+
'x-matched-path',
51+
]
52+
53+
export const filterInternalHeaders = (
54+
headers: Record<string, undefined | string | string[]>
55+
) => {
56+
for (const header in headers) {
57+
if (INTERNAL_HEADERS.includes(header)) {
58+
delete headers[header]
59+
}
60+
}
61+
}

test/e2e/app-dir/app-middleware/app-middleware.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { nextTestSetup, FileRef } from 'e2e-utils'
66
import type { Response } from 'node-fetch'
77

88
describe('app-dir with middleware', () => {
9-
const { next } = nextTestSetup({
9+
const { next, isNextDeploy } = nextTestSetup({
1010
files: __dirname,
1111
})
1212

@@ -187,6 +187,29 @@ describe('app-dir with middleware', () => {
187187
await browser.deleteCookies()
188188
})
189189

190+
// TODO: Re-enable this test in deploy mode once Vercel has proper handling
191+
if (!isNextDeploy) {
192+
it('should omit internal headers for middleware cookies', async () => {
193+
const response = await next.fetch('/rsc-cookies/cookie-options')
194+
expect(response.status).toBe(200)
195+
expect(response.headers.get('x-middleware-set-cookie')).toBeNull()
196+
})
197+
198+
it('should ignore x-middleware-set-cookie as a request header', async () => {
199+
const $ = await next.render$(
200+
'/cookies',
201+
{},
202+
{
203+
headers: {
204+
'x-middleware-set-cookie': 'test',
205+
},
206+
}
207+
)
208+
209+
expect($('#cookies').text()).toBe('cookies: 0')
210+
})
211+
}
212+
190213
it('should be possible to read cookies that are set during the middleware handling of a server action', async () => {
191214
const browser = await next.browser('/rsc-cookies')
192215
const initialRandom1 = await browser.elementById('rsc-cookie-1').text()
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { cookies } from 'next/headers'
2+
3+
export default async function Page() {
4+
const cookieLength = (await cookies()).size
5+
return <div id="cookies">cookies: {cookieLength}</div>
6+
}

test/integration/required-server-files-ssr-404/test/index.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ describe('Required Server Files', () => {
4444
}
4545
await fs.rename(join(appDir, 'pages'), join(appDir, 'pages-bak'))
4646

47+
process.env.NEXT_PRIVATE_TEST_HEADERS = '1'
4748
nextApp = nextServer({
4849
conf: {},
4950
dir: appDir,
@@ -57,6 +58,7 @@ describe('Required Server Files', () => {
5758
console.log(`Listening at ::${appPort}`)
5859
})
5960
afterAll(async () => {
61+
delete process.env.NEXT_PRIVATE_TEST_HEADERS
6062
if (server) server.close()
6163
await fs.rename(join(appDir, 'pages-bak'), join(appDir, 'pages'))
6264
})

test/production/standalone-mode/required-server-files/required-server-files-app.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ describe('required server files app router', () => {
2626
}) => {
2727
// test build against environment with next support
2828
process.env.NOW_BUILDER = nextEnv ? '1' : ''
29+
process.env.NEXT_PRIVATE_TEST_HEADERS = '1'
2930

3031
next = await createNext({
3132
files: {
@@ -97,6 +98,7 @@ describe('required server files app router', () => {
9798
await setupNext({ nextEnv: true, minimalMode: true })
9899
})
99100
afterAll(async () => {
101+
delete process.env.NEXT_PRIVATE_TEST_HEADERS
100102
await next.destroy()
101103
if (server) await killApp(server)
102104
})

test/production/standalone-mode/required-server-files/required-server-files-i18n.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ describe('required server files i18n', () => {
2525

2626
beforeAll(async () => {
2727
let wasmPkgIsAvailable = false
28+
process.env.NEXT_PRIVATE_TEST_HEADERS = '1'
2829

2930
const res = await nodeFetch(
3031
`https://registry.npmjs.com/@next/swc-wasm-nodejs/-/swc-wasm-nodejs-${
@@ -131,6 +132,7 @@ describe('required server files i18n', () => {
131132
})
132133

133134
afterAll(async () => {
135+
delete process.env.NEXT_PRIVATE_TEST_HEADERS
134136
await next.destroy()
135137
if (server) await killApp(server)
136138
})

test/production/standalone-mode/required-server-files/required-server-files-ppr.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('required server files app router', () => {
2828
}) => {
2929
// test build against environment with next support
3030
process.env.NOW_BUILDER = nextEnv ? '1' : ''
31+
process.env.NEXT_PRIVATE_TEST_HEADERS = '1'
3132

3233
next = await createNext({
3334
files: {
@@ -107,6 +108,7 @@ describe('required server files app router', () => {
107108
await setupNext({ nextEnv: true, minimalMode: true })
108109
})
109110
afterAll(async () => {
111+
delete process.env.NEXT_PRIVATE_TEST_HEADERS
110112
await next.destroy()
111113
if (server) await killApp(server)
112114
})

test/production/standalone-mode/required-server-files/required-server-files.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ describe('required server files', () => {
2929
const setupNext = async ({ nextEnv }: { nextEnv?: boolean }) => {
3030
// test build against environment with next support
3131
process.env.NOW_BUILDER = nextEnv ? '1' : ''
32+
process.env.NEXT_PRIVATE_TEST_HEADERS = '1'
3233

3334
next = await createNext({
3435
files: {
@@ -152,6 +153,7 @@ describe('required server files', () => {
152153
})
153154

154155
afterAll(async () => {
156+
delete process.env.NEXT_PRIVATE_TEST_HEADERS
155157
await next.destroy()
156158
})
157159

test/production/standalone-mode/response-cache/index.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ describe('minimal-mode-response-cache', () => {
2222
beforeAll(async () => {
2323
// test build against environment with next support
2424
process.env.NOW_BUILDER = '1'
25+
process.env.NEXT_PRIVATE_TEST_HEADERS = '1'
2526

2627
next = await createNext({
2728
files: new FileRef(join(__dirname, 'app')),
@@ -84,6 +85,7 @@ describe('minimal-mode-response-cache', () => {
8485
appPort = `http://127.0.0.1:${port}`
8586
})
8687
afterAll(async () => {
88+
delete process.env.NEXT_PRIVATE_TEST_HEADERS
8789
await next.destroy()
8890
if (server) await killApp(server)
8991
})

0 commit comments

Comments
 (0)