diff --git a/packages/next/build/analysis/get-page-static-info.ts b/packages/next/build/analysis/get-page-static-info.ts
index d27ef9057b1b..6b57ee25d94c 100644
--- a/packages/next/build/analysis/get-page-static-info.ts
+++ b/packages/next/build/analysis/get-page-static-info.ts
@@ -178,11 +178,12 @@ function getMiddlewareConfig(
function getMiddlewareRegExpStrings(
matcherOrMatchers: unknown,
- nextConfig: NextConfig
+ nextConfig: NextConfig,
+ { doNotRecurse }: { doNotRecurse?: boolean } = {}
): string[] {
if (Array.isArray(matcherOrMatchers)) {
return matcherOrMatchers.flatMap((matcher) =>
- getMiddlewareRegExpStrings(matcher, nextConfig)
+ getMiddlewareRegExpStrings(matcher, nextConfig, { doNotRecurse })
)
}
@@ -198,6 +199,13 @@ function getMiddlewareRegExpStrings(
throw new Error('`matcher`: path matcher must start with /')
}
+ if (matcher === '/' && !doNotRecurse) {
+ const rootMatcher = '/'
+ return getMiddlewareRegExpStrings([rootMatcher, '/index'], nextConfig, {
+ doNotRecurse: true,
+ })
+ }
+
if (nextConfig.i18n?.locales) {
matcher = `/:nextInternalLocale(${nextConfig.i18n.locales
.map((locale) => escapeStringRegexp(locale))
diff --git a/test/e2e/middleware-matcher/index.test.ts b/test/e2e/middleware-matcher/index.test.ts
index d76a1b5d8ed0..75defe3d5bc0 100644
--- a/test/e2e/middleware-matcher/index.test.ts
+++ b/test/e2e/middleware-matcher/index.test.ts
@@ -217,6 +217,49 @@ describe('using a single matcher', () => {
})
})
+describe('using root matcher', () => {
+ let next: NextInstance
+ beforeAll(async () => {
+ next = await createNext({
+ files: {
+ 'pages/index.js': `
+ export default function Home({ message }) {
+ return
Hi there!
+ }
+ `,
+ 'middleware.js': `
+ import { NextResponse } from 'next/server'
+ export default (req) => {
+ const res = NextResponse.next();
+ res.headers.set('X-From-Middleware', 'true');
+ return res;
+ }
+
+ export const config = { matcher: '/' };
+ `,
+ },
+ dependencies: {},
+ })
+ })
+ afterAll(() => next.destroy())
+
+ it('adds the header to the /', async () => {
+ const response = await fetchViaHTTP(next.url, '/')
+ expect(response.status).toBe(200)
+ expect(Object.fromEntries(response.headers)).toMatchObject({
+ 'x-from-middleware': 'true',
+ })
+ })
+
+ it('adds the header to the /index', async () => {
+ const response = await fetchViaHTTP(next.url, '/index')
+ expect(response.status).toBe(404)
+ expect(Object.fromEntries(response.headers)).toMatchObject({
+ 'x-from-middleware': 'true',
+ })
+ })
+})
+
describe('using a single matcher with i18n', () => {
let next: NextInstance
beforeAll(async () => {
@@ -276,6 +319,15 @@ describe('using a single matcher with i18n', () => {
expect(res2.headers.get('X-From-Middleware')).toBe('true')
})
+ it('adds the header for a mathed root path with /index', async () => {
+ const res1 = await fetchViaHTTP(next.url, `/index`)
+ expect(await res1.text()).toContain(`(en) Hello from /`)
+ expect(res1.headers.get('X-From-Middleware')).toBe('true')
+ const res2 = await fetchViaHTTP(next.url, `/es/index`)
+ expect(await res2.text()).toContain(`(es) Hello from /`)
+ expect(res2.headers.get('X-From-Middleware')).toBe('true')
+ })
+
it(`adds the headers for a matched data path`, async () => {
const res1 = await fetchViaHTTP(
next.url,
@@ -367,6 +419,15 @@ describe('using a single matcher with i18n and basePath', () => {
expect(res2.headers.get('X-From-Middleware')).toBe('true')
})
+ it('adds the header for a mathed root path with /index', async () => {
+ const res1 = await fetchViaHTTP(next.url, `/root/index`)
+ expect(await res1.text()).toContain(`(en) Hello from /`)
+ expect(res1.headers.get('X-From-Middleware')).toBe('true')
+ const res2 = await fetchViaHTTP(next.url, `/root/es/index`)
+ expect(await res2.text()).toContain(`(es) Hello from /`)
+ expect(res2.headers.get('X-From-Middleware')).toBe('true')
+ })
+
it(`adds the headers for a matched data path`, async () => {
const res1 = await fetchViaHTTP(
next.url,