Skip to content

Commit 1a026e3

Browse files
ztannerijjk
andauthored
fix router handling when setting a location response header (#82588)
Backports: - #82588 Co-authored-by: JJ Kasper <[email protected]>
1 parent be4aafd commit 1a026e3

File tree

3 files changed

+68
-10
lines changed

3 files changed

+68
-10
lines changed

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

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ import { formatHostname } from '../format-hostname'
1818
import { toNodeOutgoingHttpHeaders } from '../../web/utils'
1919
import { isAbortError } from '../../pipe-readable'
2020
import { getHostname } from '../../../shared/lib/get-hostname'
21-
import { getRedirectStatus } from '../../../lib/redirect-status'
21+
import {
22+
getRedirectStatus,
23+
allowedStatusCodes,
24+
} from '../../../lib/redirect-status'
2225
import { normalizeRepeatedSlashes } from '../../../shared/lib/utils'
2326
import { getRelativeURL } from '../../../shared/lib/router/utils/relativize-url'
2427
import { addPathPrefix } from '../../../shared/lib/router/utils/add-path-prefix'
@@ -657,15 +660,36 @@ export function getResolveRoutes(
657660

658661
if (middlewareHeaders['location']) {
659662
const value = middlewareHeaders['location'] as string
660-
const rel = getRelativeURL(value, initUrl)
661-
resHeaders['location'] = rel
662-
parsedUrl = url.parse(rel, true)
663663

664-
return {
665-
parsedUrl,
666-
resHeaders,
667-
finished: true,
668-
statusCode: middlewareRes.status,
664+
// Only process Location header as a redirect if it has a proper redirect status
665+
// This prevents a Location header with non-redirect status from being treated as a redirect
666+
const isRedirectStatus = allowedStatusCodes.has(
667+
middlewareRes.status
668+
)
669+
670+
if (isRedirectStatus) {
671+
// Process as redirect: update parsedUrl and convert to relative URL
672+
const rel = getRelativeURL(value, initUrl)
673+
resHeaders['location'] = rel
674+
parsedUrl = url.parse(rel, true)
675+
676+
return {
677+
parsedUrl,
678+
resHeaders,
679+
finished: true,
680+
statusCode: middlewareRes.status,
681+
}
682+
} else {
683+
// Not a redirect: just pass through the Location header
684+
resHeaders['location'] = value
685+
686+
return {
687+
parsedUrl,
688+
resHeaders,
689+
finished: true,
690+
bodyStream,
691+
statusCode: middlewareRes.status,
692+
}
669693
}
670694
}
671695

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

@@ -248,6 +248,29 @@ describe('app-dir with middleware', () => {
248248

249249
await browser.deleteCookies()
250250
})
251+
252+
// TODO: This consistently 404s on Vercel deployments. It technically
253+
// doesn't repro the bug we're trying to fix but we need to figure out
254+
// why the handling is different.
255+
if (!isNextDeploy) {
256+
it('should not incorrectly treat a Location header as a rewrite', async () => {
257+
const res = await next.fetch('/test-location-header')
258+
259+
// Should get status 200 (not a redirect status)
260+
expect(res.status).toBe(200)
261+
262+
// Should get the JSON response associated with the route,
263+
// and not follow the redirect
264+
const json = await res.json()
265+
expect(json).toEqual({ foo: 'bar' })
266+
267+
// Ensure the provided location is still on the response
268+
const locationHeader = res.headers.get('location')
269+
expect(locationHeader).toBe(
270+
'https://next-data-api-endpoint.vercel.app/api/random'
271+
)
272+
})
273+
}
251274
})
252275

253276
describe('app dir - middleware without pages dir', () => {

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ export async function middleware(request) {
7878
return res
7979
}
8080

81+
if (request.nextUrl.pathname === '/test-location-header') {
82+
return NextResponse.json(
83+
{ foo: 'bar' },
84+
{
85+
headers: {
86+
location: 'https://next-data-api-endpoint.vercel.app/api/random',
87+
},
88+
}
89+
)
90+
}
91+
8192
return NextResponse.next({
8293
request: {
8394
headers: headersFromRequest,

0 commit comments

Comments
 (0)