Skip to content

Commit 93da5ee

Browse files
committed
Skip internal routes in middleware
1 parent fbb50f4 commit 93da5ee

File tree

6 files changed

+79
-15
lines changed

6 files changed

+79
-15
lines changed

packages/next/src/build/webpack-config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,10 @@ export default async function getBaseWebpackConfig(
20932093
dev,
20942094
sriEnabled: !dev && !!config.experimental.sri?.algorithm,
20952095
rewrites,
2096+
nextConfig: {
2097+
basePath: config.basePath,
2098+
skipMiddlewareNextInternalRoutes: config.skipMiddlewareNextInternalRoutes,
2099+
},
20962100
edgeEnvironments: {
20972101
__NEXT_BUILD_ID: buildId,
20982102
NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: encryptionKey,

packages/next/src/build/webpack/plugins/middleware-plugin.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
import type { EdgeSSRMeta } from '../loaders/get-module-build-info'
66
import type { MiddlewareMatcher } from '../../analysis/get-page-static-info'
77
import { getNamedMiddlewareRegex } from '../../../shared/lib/router/utils/route-regex'
8+
import { getDefaultMiddlewareMatchers } from '../../../server/lib/router-utils/filesystem'
89
import { getModuleBuildInfo } from '../loaders/get-module-build-info'
910
import { getSortedRoutes } from '../../../shared/lib/router/utils'
1011
import { webpack, sources } from 'next/dist/compiled/webpack/webpack'
@@ -36,6 +37,7 @@ import type { CustomRoutes } from '../../../lib/load-custom-routes'
3637
import { isInterceptionRouteRewrite } from '../../../lib/generate-interception-routes-rewrites'
3738
import { getDynamicCodeEvaluationError } from './wellknown-errors-plugin/parse-dynamic-code-evaluation-error'
3839
import { getModuleReferencesInOrder } from '../utils'
40+
import type { NextConfig } from '../../../server/config-shared'
3941

4042
const KNOWN_SAFE_DYNAMIC_PACKAGES =
4143
require('../../../lib/known-edge-safe-packages.json') as string[]
@@ -204,12 +206,17 @@ function getCreateAssets(params: {
204206
const { namedRegex } = getNamedMiddlewareRegex(matcherSource, {
205207
catchAll,
206208
})
207-
const matchers = metadata?.edgeMiddleware?.matchers ?? [
208-
{
209-
regexp: namedRegex,
210-
originalSource: page === '/' && catchAll ? '/:path*' : matcherSource,
211-
},
212-
]
209+
210+
const matchers =
211+
metadata?.edgeMiddleware?.matchers ??
212+
(page === '/' && catchAll
213+
? getDefaultMiddlewareMatchers({
214+
basePath: opts.nextConfig.basePath ?? '',
215+
skipMiddlewareNextInternalRoutes: Boolean(
216+
opts.nextConfig.skipMiddlewareNextInternalRoutes
217+
),
218+
})
219+
: [{ regexp: namedRegex, originalSource: matcherSource }])
213220

214221
const isEdgeFunction = !!(metadata.edgeApiFunction || metadata.edgeSSR)
215222
const edgeFunctionDefinition: EdgeFunctionDefinition = {
@@ -818,17 +825,26 @@ interface Options {
818825
sriEnabled: boolean
819826
rewrites: CustomRoutes['rewrites']
820827
edgeEnvironments: EdgeRuntimeEnvironments
828+
nextConfig: Pick<NextConfig, 'basePath' | 'skipMiddlewareNextInternalRoutes'>
821829
}
822830

823831
export default class MiddlewarePlugin {
824832
private readonly dev: Options['dev']
825833
private readonly sriEnabled: Options['sriEnabled']
826834
private readonly rewrites: Options['rewrites']
827835
private readonly edgeEnvironments: EdgeRuntimeEnvironments
828-
829-
constructor({ dev, sriEnabled, rewrites, edgeEnvironments }: Options) {
836+
private readonly nextConfig: Options['nextConfig']
837+
838+
constructor({
839+
dev,
840+
sriEnabled,
841+
rewrites,
842+
edgeEnvironments,
843+
nextConfig,
844+
}: Options) {
830845
this.dev = dev
831846
this.sriEnabled = sriEnabled
847+
this.nextConfig = nextConfig
832848
this.rewrites = rewrites
833849
this.edgeEnvironments = edgeEnvironments
834850
}
@@ -886,6 +902,7 @@ export default class MiddlewarePlugin {
886902
rewrites: this.rewrites,
887903
edgeEnvironments: this.edgeEnvironments,
888904
dev: this.dev,
905+
nextConfig: this.nextConfig,
889906
},
890907
})
891908
)

packages/next/src/server/config-schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ export const configSchema: zod.ZodType<NextConfig> = z.lazy(() =>
689689
serverExternalPackages: z.array(z.string()).optional(),
690690
serverRuntimeConfig: z.record(z.string(), z.any()).optional(),
691691
skipMiddlewareUrlNormalize: z.boolean().optional(),
692+
skipMiddlewareNextInternalRoutes: z.boolean().optional(),
692693
skipTrailingSlashRedirect: z.boolean().optional(),
693694
staticPageGenerationTimeout: z.number().optional(),
694695
expireTime: z.number().optional(),

packages/next/src/server/config-shared.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,12 @@ export interface NextConfig {
12411241

12421242
skipMiddlewareUrlNormalize?: boolean
12431243

1244+
/**
1245+
* Skip Next.js internals route `/_next` from middleware.
1246+
* @default true
1247+
*/
1248+
skipMiddlewareNextInternalRoutes?: boolean
1249+
12441250
skipTrailingSlashRedirect?: boolean
12451251

12461252
modularizeImports?: Record<
@@ -1525,6 +1531,7 @@ export const defaultConfig = Object.freeze({
15251531
},
15261532
htmlLimitedBots: undefined,
15271533
bundlePagesRouterDependencies: false,
1534+
skipMiddlewareNextInternalRoutes: true,
15281535
} satisfies NextConfig)
15291536

15301537
export async function normalizeConfig(phase: string, config: any) {

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

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { pathHasPrefix } from '../../../shared/lib/router/utils/path-has-prefix'
3131
import { normalizeLocalePath } from '../../../shared/lib/i18n/normalize-locale-path'
3232
import { removePathPrefix } from '../../../shared/lib/router/utils/remove-path-prefix'
3333
import { getMiddlewareRouteMatcher } from '../../../shared/lib/router/utils/middleware-route-matcher'
34+
import type { MiddlewareMatcher } from '../../../build/analysis/get-page-static-info'
3435
import {
3536
APP_PATH_ROUTES_MANIFEST,
3637
BUILD_ID_FILE,
@@ -47,6 +48,34 @@ import { PrefetchRSCPathnameNormalizer } from '../../normalizers/request/prefetc
4748
import { encodeURIPath } from '../../../shared/lib/encode-uri-path'
4849
import { isMetadataRouteFile } from '../../../lib/metadata/is-metadata-route'
4950

51+
/**
52+
* Generate default middleware matchers that exclude /_next/ internal routes
53+
*/
54+
export function getDefaultMiddlewareMatchers(
55+
config: Pick<
56+
NextConfigComplete,
57+
'basePath' | 'skipMiddlewareNextInternalRoutes'
58+
>
59+
): MiddlewareMatcher[] {
60+
// If skipMiddlewareNextInternalRoutes is explicitly set to false, match everything
61+
if (config.skipMiddlewareNextInternalRoutes === false) {
62+
return [{ regexp: '.*', originalSource: '/:path*' }]
63+
}
64+
65+
const basePath = config.basePath || ''
66+
// Build the exclusion regex directly - matches all paths except /_next/
67+
const excludeRegex = basePath
68+
? `^${basePath}/(?!_next/).*$`
69+
: `^/(?!_next/).*$`
70+
71+
return [
72+
{
73+
regexp: excludeRegex,
74+
originalSource: '/((?!_next/).*)',
75+
},
76+
]
77+
}
78+
5079
export type FsOutput = {
5180
type:
5281
| 'appFile'
@@ -311,11 +340,15 @@ export async function setupFsCheck(opts: {
311340
middlewareMatcher = getMiddlewareRouteMatcher(
312341
middlewareManifest.middleware?.['/']?.matchers
313342
)
343+
} else if (middlewareManifest.middleware?.['/']) {
344+
// Middleware exists but has no matchers - use default with exclusions
345+
middlewareMatcher = getMiddlewareRouteMatcher(
346+
getDefaultMiddlewareMatchers(opts.config)
347+
)
314348
} else if (functionsConfigManifest?.functions['/_middleware']) {
315349
middlewareMatcher = getMiddlewareRouteMatcher(
316-
functionsConfigManifest.functions['/_middleware'].matchers ?? [
317-
{ regexp: '.*', originalSource: '/:path*' },
318-
]
350+
functionsConfigManifest.functions['/_middleware'].matchers ??
351+
getDefaultMiddlewareMatchers(opts.config)
319352
)
320353
}
321354

packages/next/src/server/lib/router-utils/setup-dev-bundler.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import path from 'path'
1414
import qs from 'querystring'
1515
import Watchpack from 'next/dist/compiled/watchpack'
1616
import findUp from 'next/dist/compiled/find-up'
17-
import { buildCustomRoute } from './filesystem'
17+
import { buildCustomRoute, getDefaultMiddlewareMatchers } from './filesystem'
1818
import * as Log from '../../../build/output/log'
1919
import { setGlobal } from '../../../trace/shared'
2020
import type { Telemetry } from '../../../telemetry/storage'
@@ -471,9 +471,11 @@ async function startWatcher(
471471
'actualMiddlewareFile',
472472
serverFields.actualMiddlewareFile
473473
)
474-
middlewareMatchers = staticInfo.middleware?.matchers || [
475-
{ regexp: '^/.*$', originalSource: '/:path*' },
476-
]
474+
middlewareMatchers = staticInfo.middleware?.matchers ||
475+
getDefaultMiddlewareMatchers({
476+
basePath: nextConfig.basePath,
477+
skipMiddlewareNextInternalRoutes: nextConfig.skipMiddlewareNextInternalRoutes
478+
})
477479
continue
478480
}
479481
if (isInstrumentationHookFile(rootFile)) {

0 commit comments

Comments
 (0)