Skip to content

Commit 9c6ec9f

Browse files
piehkodiakhq[bot]
andauthored
fix: preserve original path after middleware rewrite (#2177)
* test: add test case * fix: preserve original path after middleware rewrite --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent c327628 commit 9c6ec9f

File tree

7 files changed

+50
-3
lines changed

7 files changed

+50
-3
lines changed

cypress/e2e/middleware/standard.cy.ts

+6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ describe('Standard middleware', () => {
4545
expect(response.headers).to.have.property('x-foo', 'bar')
4646
})
4747
})
48+
49+
it('preserves locale on rewrites (skipMiddlewareUrlNormalize: true)', () => {
50+
cy.visit('/de-de/locale-preserving-rewrite')
51+
cy.get('div').should('contain', 'Locale: de-DE')
52+
cy.url().should('eq', `${Cypress.config().baseUrl}/de-de/locale-preserving-rewrite`)
53+
})
4854
})
4955

5056
describe('Middleware matchers', () => {

demos/middleware/middleware.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ export async function middleware(req: NextRequest) {
2121
}
2222

2323
const request = new MiddlewareRequest(req)
24-
if (pathname.startsWith('/static')) {
24+
25+
// skipMiddlewareUrlNormalize next config option is used so we have to try to match both html path and data blob path
26+
if (pathname.startsWith('/static') || pathname.endsWith('/static.json')) {
2527
// Unlike NextResponse.next(), this actually sends the request to the origin
2628
const res = await request.next()
2729
const message = `This was static (& escaping test &amp;) but has been transformed in ${req.geo?.city}`
@@ -36,7 +38,8 @@ export async function middleware(req: NextRequest) {
3638
return res
3739
}
3840

39-
if (pathname.startsWith('/request-rewrite')) {
41+
// skipMiddlewareUrlNormalize next config option is used so we have to try to match both html path and data blob path
42+
if (pathname.startsWith('/request-rewrite') || pathname.endsWith('/request-rewrite.json')) {
4043
// request.rewrite() should return the MiddlewareResponse object instead of the Response object.
4144
const res = await request.rewrite('/static-rewrite')
4245
const message = `This was static (& escaping test &amp;) but has been transformed in ${req.geo?.city}`
@@ -100,6 +103,10 @@ export async function middleware(req: NextRequest) {
100103
return response
101104
}
102105

106+
if (pathname.includes('locale-preserving-rewrite')) {
107+
return NextResponse.rewrite(new URL('/locale-test', req.url))
108+
}
109+
103110
if (pathname.startsWith('/shows')) {
104111
if (pathname.startsWith('/shows/222')) {
105112
response = NextResponse.next()
@@ -151,6 +158,7 @@ export const config = {
151158
matcher: [
152159
'/api/:all*',
153160
'/headers',
161+
'/:all*/locale-preserving-rewrite',
154162
'/cookies/:path*',
155163
{ source: '/static' },
156164
{source: '/request-rewrite' },

demos/middleware/next.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const nextConfig = {
1111
defaultLocale: 'en',
1212
locales: ['en', 'de-DE'],
1313
},
14+
skipMiddlewareUrlNormalize: true,
1415
}
1516

1617
module.exports = nextConfig

demos/middleware/pages/locale-test.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as React from 'react'
2+
3+
const Page = ({ pageLocale }) => {
4+
return <div>Locale: {pageLocale}</div>
5+
}
6+
7+
export async function getServerSideProps({ locale }) {
8+
return {
9+
props: {
10+
pageLocale: locale,
11+
},
12+
}
13+
}
14+
15+
export default Page

packages/runtime/src/templates/edge-shared/utils.ts

+3
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ export const buildResponse = async ({
270270
}
271271
res.headers.set('x-middleware-rewrite', relativeUrl)
272272

273+
request.headers.set('x-original-path', new URL(request.url, `http://n`).pathname)
274+
request.headers.set('x-middleware-rewrite', rewrite)
275+
273276
return addMiddlewareHeaders(context.rewrite(rewrite), res)
274277
}
275278

packages/runtime/src/templates/handlerUtils.ts

+7
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,13 @@ export const normalizePath = (event: HandlerEvent) => {
189189
return originalPath
190190
}
191191
}
192+
193+
if (event.headers['x-original-path']) {
194+
if (event.headers['x-next-debug-logging']) {
195+
console.log('Original path:', event.headers['x-original-path'])
196+
}
197+
return event.headers['x-original-path']
198+
}
192199
// Ensure that paths are encoded - but don't double-encode them
193200
return new URL(event.rawUrl).pathname
194201
}

packages/runtime/src/templates/server.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { PrerenderManifest } from 'next/dist/build'
1+
// eslint-disable-next-line n/no-deprecated-api -- this is what Next.js uses as well
2+
import { parse } from 'url'
3+
4+
import type { PrerenderManifest } from 'next/dist/build'
25
import type { BaseNextResponse } from 'next/dist/server/base-http'
36
import type { NodeRequestHandler, Options } from 'next/dist/server/next-server'
47

@@ -36,6 +39,10 @@ const getNetlifyNextServer = (NextServer: NextServerType) => {
3639
public getRequestHandler(): NodeRequestHandler {
3740
const handler = super.getRequestHandler()
3841
return async (req, res, parsedUrl) => {
42+
if (!parsedUrl && typeof req?.headers?.['x-middleware-rewrite'] === 'string') {
43+
parsedUrl = parse(req.headers['x-middleware-rewrite'], true)
44+
}
45+
3946
// preserve the URL before Next.js mutates it for i18n
4047
const { url, headers } = req
4148

0 commit comments

Comments
 (0)