diff --git a/packages/sitecore-jss-nextjs/src/middleware/redirects-middleware.test.ts b/packages/sitecore-jss-nextjs/src/middleware/redirects-middleware.test.ts index c7f8ab7e19..5afee7728e 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/redirects-middleware.test.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/redirects-middleware.test.ts @@ -1430,6 +1430,62 @@ describe('RedirectsMiddleware', () => { }); }); + it('should remove x-middleware-next header and redirect 301', async () => { + const siteName = 'foo'; + const res = NextResponse.redirect('http://localhost:3000/found', {}); + res.headers.set('x-middleware-next', '1'); + res.cookies.set('sc_site', siteName); + const req = createRequest({ + nextUrl: { + href: 'http://localhost:3000/not-found', + pathname: '/not-found', + locale: 'en', + search: '', + origin: 'http://localhost:3000', + clone() { + return Object.assign({}, req.nextUrl); + }, + }, + }); + + const { middleware, fetchRedirects, siteResolver } = createMiddleware({ + pattern: 'not-found', + target: '/found', + redirectType: REDIRECT_TYPE_301, + isQueryStringPreserved: true, + locale: 'en', + }); + + const expected = NextResponse.redirect('http://localhost:3000/found', { + ...res, + status: 301, + headers: {}, + }); + + const finalRes = await middleware.getHandler()(req, res); + + validateDebugLog('redirects middleware start: %o', { + hostname: 'foo.net', + language: 'en', + pathname: '/not-found', + }); + + validateEndMessageDebugLog('redirects middleware end in %dms: %o', { + headers: { + location: 'http://localhost:3000/found', + 'set-cookie': 'sc_site=foo; Path=/', + }, + redirected: false, + status: 301, + url: '', + }); + + expect(siteResolver.getByHost).not.called.to.equal(true); + expect(siteResolver.getByName).to.be.calledWith(siteName); + expect(fetchRedirects).to.be.calledWith(siteName); + expect(finalRes.status).to.equal(expected.status); + }); + describe('should redirect to normalized path when nextjs specific "path" query string parameter is provided', () => { it('should return 301 redirect', async () => { const setCookies = () => {}; diff --git a/packages/sitecore-jss-nextjs/src/middleware/redirects-middleware.ts b/packages/sitecore-jss-nextjs/src/middleware/redirects-middleware.ts index 87bea32615..cfd9fcd865 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/redirects-middleware.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/redirects-middleware.ts @@ -143,38 +143,16 @@ export class RedirectsMiddleware extends MiddlewareBase { url.href = prepareNewURL.href; } - const normalizedHeaders = new Headers(); - - /** - * FIX for Netlify service - * Netlify doesn’t handle 301/302 redirects if Next.js sets the header x-middleware-next. - */ - if (res?.headers) { - res.headers.forEach((value, key) => { - if (key.toLowerCase() !== 'x-middleware-next') { - normalizedHeaders.set(key, value); - } - }); - } - const redirectUrl = decodeURIComponent(url.href); /** return Response redirect with http code of redirect type **/ switch (existsRedirect.redirectType) { - case REDIRECT_TYPE_301: - return NextResponse.redirect(redirectUrl, { - ...res, - status: 301, - statusText: 'Moved Permanently', - headers: normalizedHeaders, - }); - case REDIRECT_TYPE_302: - return NextResponse.redirect(redirectUrl, { - ...res, - status: 302, - statusText: 'Found', - headers: normalizedHeaders, - }); + case REDIRECT_TYPE_301: { + return this.createRedirectResponse(redirectUrl, res, 301, 'Moved Permanently'); + } + case REDIRECT_TYPE_302: { + return this.createRedirectResponse(redirectUrl, res, 302, 'Found'); + } case REDIRECT_TYPE_SERVER_TRANSFER: { return this.rewrite(redirectUrl, req, res || NextResponse.next()); } @@ -281,4 +259,30 @@ export class RedirectsMiddleware extends MiddlewareBase { return new URL(`${url.pathname}`, url.origin); } + + /** + * Helper function to create a redirect response and remove the x-middleware-next header. + * @param {string} url The URL to redirect to. + * @param {Response} res The response object. + * @param {number} status The HTTP status code of the redirect. + * @param {string} statusText The status text of the redirect. + * @returns {NextResponse} The redirect response. + */ + private createRedirectResponse( + url: string, + res: Response | undefined, + status: number, + statusText: string + ): NextResponse { + const redirect = NextResponse.redirect(url, { + ...(res || {}), + status, + statusText, + headers: res?.headers, + }); + if (res?.headers) { + redirect.headers.delete('x-middleware-next'); + } + return redirect; + } }