Repository: vercel/next.js
Pull request: Fix next-routing header interpolation

## Summary
- interpolate source and `has` captures in `@next/routing` route headers anywhere destinations are already interpolated
- make `onMatch` headers respect route matching and capture interpolation instead of applying unconditionally
- keep middleware and initial request headers internal to routing so `resolvedHeaders` only contains actual response headers
- add regressions for header interpolation and for request-header leakage into `resolvedHeaders`

## Testing
- `pnpm --dir packages/next-routing types`
- `pnpm --dir packages/next-routing test -- --runInBand`


Changed files:
filename: packages/next-routing/src/__tests__/captures.test.ts
patch: @@ -563,6 +563,137 @@ describe('Has Condition Captures in Destination', () => {
     expect(result.redirect).toBeDefined()
     expect(result.redirect?.url.pathname).toBe('/es/home')
   })
+
+  it('should interpolate source and has captures in route headers', async () => {
+    const headers = new Headers({
+      'x-language': 'es',
+    })
+
+    const params = createBaseParams({
+      url: new URL('https://example.com/users/123'),
+      headers,
+      routes: {
+        beforeMiddleware: [
+          {
+            sourceRegex: '^/users/([^/]+)$',
+            headers: {
+              Location: '/profiles/$1',
+              'x-user-id': '$1',
+              'x-language': '$xlanguage',
+              'x-combined': '$1-$xlanguage',
+            },
+            has: [
+              {
+                type: 'header',
+                key: 'x-language',
+              },
+            ],
+          },
+        ],
+        beforeFiles: [],
+        afterFiles: [],
+        dynamicRoutes: [],
+        onMatch: [],
+        fallback: [],
+      },
+      pathnames: ['/users/123'],
+    })
+
+    const result = await resolveRoutes(params)
+
+    expect(result.matchedPathname).toBe('/users/123')
+    expect(result.resolvedHeaders?.get('Location')).toBe('/profiles/123')
+    expect(result.resolvedHeaders?.get('x-user-id')).toBe('123')
+    expect(result.resolvedHeaders?.get('x-language')).toBe('es')
+    expect(result.resolvedHeaders?.get('x-combined')).toBe('123-es')
+  })
+
+  it('should interpolate source and has captures in onMatch headers', async () => {
+    const headers = new Headers({
+      'x-language': 'fr',
+    })
+
+    const params = createBaseParams({
+      url: new URL('https://example.com/api/posts'),
+      headers,
+      routes: {
+        beforeMiddleware: [],
+        beforeFiles: [],
+        afterFiles: [],
+        dynamicRoutes: [],
+        onMatch: [
+          {
+            sourceRegex: '^/api/(?<resource>[^/]+)$',
+            headers: {
+              'x-resource': '$resource',
+              'x-language': '$xlanguage',
+              'x-combined': '$resource-$xlanguage',
+            },
+            has: [
+              {
+                type: 'header',
+                key: 'x-language',
+              },
+            ],
+          },
+          {
+            sourceRegex: '^/admin$',
+            headers: {
+              'x-should-not-match': 'true',
+            },
+          },
+        ],
+        fallback: [],
+      },
+      pathnames: ['/api/posts'],
+    })
+
+    const result = await resolveRoutes(params)
+
+    expect(result.matchedPathname).toBe('/api/posts')
+    expect(result.resolvedHeaders?.get('x-resource')).toBe('posts')
+    expect(result.resolvedHeaders?.get('x-language')).toBe('fr')
+    expect(result.resolvedHeaders?.get('x-combined')).toBe('posts-fr')
+    expect(result.resolvedHeaders?.get('x-should-not-match')).toBeNull()
+  })
+
+  it('should interpolate has captures in dynamic route destinations', async () => {
+    const headers = new Headers({
+      'x-language': 'fr',
+    })
+
+    const params = createBaseParams({
+      url: new URL('https://example.com/profile/john'),
+      headers,
+      routes: {
+        beforeMiddleware: [],
+        beforeFiles: [],
+        afterFiles: [],
+        dynamicRoutes: [
+          {
+            sourceRegex: '^/profile/([^/]+)$',
+            destination: '/$xlanguage/profile/$1',
+            has: [
+              {
+                type: 'header',
+                key: 'x-language',
+              },
+            ],
+          },
+        ],
+        onMatch: [],
+        fallback: [],
+      },
+      pathnames: ['/fr/profile/john'],
+    })
+
+    const result = await resolveRoutes(params)
+
+    expect(result.matchedPathname).toBe('/fr/profile/john')
+    expect(result.routeMatches).toEqual({
+      '1': 'john',
+    })
+  })
 })
 
 describe('Complex Capture Scenarios', () => {

filename: packages/next-routing/src/__tests__/resolve-routes.test.ts
patch: @@ -223,24 +223,90 @@ describe('resolveRoutes - invokeMiddleware', () => {
     expect(result.externalRewrite?.toString()).toBe('https://external.com/api')
   })
 
-  it('should apply requestHeaders from middleware', async () => {
-    const newHeaders = new Headers({
+  it('should use requestHeaders from middleware for downstream routing without returning them', async () => {
+    const middlewareRequestHeaders = new Headers({
       'x-custom-header': 'middleware-value',
     })
 
     const params = createBaseParams({
       url: new URL('https://example.com/test'),
       invokeMiddleware: async () => ({
-        requestHeaders: newHeaders,
+        requestHeaders: middlewareRequestHeaders,
+      }),
+      routes: {
+        beforeMiddleware: [],
+        beforeFiles: [
+          {
+            sourceRegex: '^/test$',
+            destination: '/internal',
+            has: [
+              {
+                type: 'header',
+                key: 'x-custom-header',
+                value: 'middleware-value',
+              },
+            ],
+          },
+        ],
+        afterFiles: [],
+        dynamicRoutes: [],
+        onMatch: [],
+        fallback: [],
+      },
+      pathnames: ['/internal'],
+    })
+
+    const result = await resolveRoutes(params)
+
+    expect(result.matchedPathname).toBe('/internal')
+    expect(result.resolvedHeaders?.get('x-custom-header')).toBeNull()
+  })
+
+  it('should return middleware responseHeaders without leaking request headers', async () => {
+    const middlewareRequestHeaders = new Headers({
+      'x-internal-header': 'middleware-only',
+    })
+    const middlewareResponseHeaders = new Headers({
+      'x-response-header': 'response-value',
+    })
+
+    const params = createBaseParams({
+      url: new URL('https://example.com/test'),
+      headers: new Headers({
+        authorization: 'Bearer secret',
+      }),
+      invokeMiddleware: async () => ({
+        requestHeaders: middlewareRequestHeaders,
+        responseHeaders: middlewareResponseHeaders,
       }),
       pathnames: ['/test'],
     })
 
     const result = await resolveRoutes(params)
 
-    expect(result.resolvedHeaders?.get('x-custom-header')).toBe(
-      'middleware-value'
+    expect(result.matchedPathname).toBe('/test')
+    expect(result.resolvedHeaders?.get('x-response-header')).toBe(
+      'response-value'
     )
+    expect(result.resolvedHeaders?.get('x-internal-header')).toBeNull()
+    expect(result.resolvedHeaders?.get('authorization')).toBeNull()
+  })
+
+  it('should not return initial request headers in resolvedHeaders', async () => {
+    const params = createBaseParams({
+      url: new URL('https://example.com/test'),
+      headers: new Headers({
+        authorization: 'Bearer secret',
+        'x-request-id': 'req-123',
+      }),
+      pathnames: ['/test'],
+    })
+
+    const result = await resolveRoutes(params)
+
+    expect(result.matchedPathname).toBe('/test')
+    expect(result.resolvedHeaders?.get('authorization')).toBeNull()
+    expect(result.resolvedHeaders?.get('x-request-id')).toBeNull()
   })
 })
 

filename: packages/next-routing/src/resolve-routes.ts
patch: @@ -20,6 +20,7 @@ function matchRoute(
 ): {
   matched: boolean
   destination?: string
+  headers?: Record<string, string>
   regexMatches?: RegExpMatchArray
   hasCaptures?: Record<string, string>
 } {
@@ -47,10 +48,19 @@ function matchRoute(
   const destination = route.destination
     ? replaceDestination(route.destination, regexMatches, hasResult.captures)
     : undefined
+  const resolvedHeaders = route.headers
+    ? Object.fromEntries(
+        Object.entries(route.headers).map(([key, value]) => [
+          replaceDestination(key, regexMatches, hasResult.captures),
+          replaceDestination(value, regexMatches, hasResult.captures),
+        ])
+      )
+    : undefined
 
   return {
     matched: true,
     destination,
+    headers: resolvedHeaders,
     regexMatches,
     hasCaptures: hasResult.captures,
   }
@@ -62,7 +72,8 @@ function matchRoute(
 function processRoutes(
   routes: Route[],
   url: URL,
-  headers: Headers,
+  requestHeaders: Headers,
+  responseHeaders: Headers,
   initialOrigin: string
 ): {
   url: URL
@@ -78,12 +89,12 @@ function processRoutes(
   let currentStatus: number | undefined
 
   for (const route of routes) {
-    const match = matchRoute(route, currentUrl, headers)
+    const match = matchRoute(route, currentUrl, requestHeaders)
 
     if (match.matched) {
-      if (route.headers) {
-        for (const [key, value] of Object.entries(route.headers)) {
-          headers.set(key, value)
+      if (match.headers) {
+        for (const [key, value] of Object.entries(match.headers)) {
+          responseHeaders.set(key, value)
         }
       }
 
@@ -95,8 +106,8 @@ function processRoutes(
         // Check if route has redirect status and Location/Refresh header
         if (
           isRedirectStatus(route.status) &&
-          route.headers &&
-          hasRedirectHeaders(route.headers)
+          match.headers &&
+          hasRedirectHeaders(match.headers)
         ) {
           const redirectUrl = isExternalDestination(match.destination)
             ? new URL(match.destination)
@@ -166,7 +177,7 @@ function matchDynamicRoute(
 ): {
   matched: boolean
   params?: Record<string, string>
-  destinationPathname?: string
+  regexMatches?: RegExpMatchArray
 } {
   const regex = new RegExp(route.sourceRegex)
   const match = pathname.match(regex)
@@ -189,27 +200,25 @@ function matchDynamicRoute(
     Object.assign(params, match.groups)
   }
 
-  // Compute destination pathname by applying the destination template
-  let destinationPathname: string | undefined
-  if (route.destination) {
-    const replaced = replaceDestination(route.destination, match, {})
-    // Extract just the pathname part (before any query string)
-    const [pathPart] = replaced.split('?')
-    destinationPathname = pathPart
-  }
-
-  return { matched: true, params, destinationPathname }
+  return { matched: true, params, regexMatches: match }
 }
 
 /**
  * Applies headers from onMatch routes
  */
-function applyOnMatchHeaders(routes: Route[], headers: Headers): Headers {
-  const newHeaders = new Headers(headers)
+function applyOnMatchHeaders(
+  routes: Route[],
+  url: URL,
+  requestHeaders: Headers,
+  responseHeaders: Headers
+): Headers {
+  const newHeaders = new Headers(responseHeaders)
 
   for (const route of routes) {
-    if (route.headers) {
-      for (const [key, value] of Object.entries(route.headers)) {
+    const match = matchRoute(route, url, requestHeaders)
+
+    if (match.matched && match.headers) {
+      for (const [key, value] of Object.entries(match.headers)) {
         newHeaders.set(key, value)
       }
     }
@@ -225,7 +234,8 @@ function checkDynamicRoutes(
   dynamicRoutes: Route[],
   url: URL,
   pathnames: string[],
-  headers: Headers,
+  requestHeaders: Headers,
+  responseHeaders: Headers,
   onMatchRoutes: Route[],
   basePath: string,
   buildId: string,
@@ -247,20 +257,31 @@ function checkDynamicRoutes(
 
     if (match.matched) {
       // Check has/missing conditions
-      const hasResult = checkHasConditions(route.has, checkUrl, headers)
+      const hasResult = checkHasConditions(route.has, checkUrl, requestHeaders)
       const missingMatched = checkMissingConditions(
         route.missing,
         checkUrl,
-        headers
+        requestHeaders
       )
 
       if (hasResult.matched && missingMatched) {
         // Check if the destination pathname (template path) is in the provided pathnames list
         // For dynamic routes, the destination contains the template path like /dynamic/[slug]
-        const pathnameToCheck = match.destinationPathname || checkUrl.pathname
+        const pathnameToCheck = route.destination
+          ? replaceDestination(
+              route.destination,
+              match.regexMatches || null,
+              hasResult.captures
+            ).split('?')[0]
+          : checkUrl.pathname
         const matchedPath = matchesPathname(pathnameToCheck, pathnames)
         if (matchedPath) {
-          const finalHeaders = applyOnMatchHeaders(onMatchRoutes, headers)
+          const finalHeaders = applyOnMatchHeaders(
+            onMatchRoutes,
+            checkUrl,
+            requestHeaders,
+            responseHeaders
+          )
           return {
             matched: true,
             result: {
@@ -296,7 +317,8 @@ export async function resolveRoutes(
   const { shouldNormalizeNextData } = routes
 
   let currentUrl = new URL(initialUrl.toString())
-  let currentHeaders = new Headers(initialHeaders)
+  let currentRequestHeaders = new Headers(initialHeaders)
+  let currentResponseHeaders = new Headers()
   let currentStatus: number | undefined
   const initialOrigin = initialUrl.origin
 
@@ -320,9 +342,9 @@ export async function resolveRoutes(
     // Skip locale handling for _next and api routes
     if (!pathname.startsWith('/_next/') && !pathname.startsWith('/api/')) {
       const hostname = currentUrl.hostname
-      const cookieHeader = currentHeaders.get('cookie') || undefined
+      const cookieHeader = currentRequestHeaders.get('cookie') || undefined
       const acceptLanguageHeader =
-        currentHeaders.get('accept-language') || undefined
+        currentRequestHeaders.get('accept-language') || undefined
 
       // Detect locale from path first
       const pathLocaleResult = normalizeLocalePath(pathname, i18n.locales)
@@ -371,7 +393,7 @@ export async function resolveRoutes(
                 url: redirectUrl,
                 status: 307,
               },
-              resolvedHeaders: currentHeaders,
+              resolvedHeaders: currentResponseHeaders,
             }
           }
 
@@ -389,7 +411,7 @@ export async function resolveRoutes(
                 url: redirectUrl,
                 status: 307,
               },
-              resolvedHeaders: currentHeaders,
+              resolvedHeaders: currentResponseHeaders,
             }
           }
         }
@@ -408,7 +430,8 @@ export async function resolveRoutes(
   const beforeMiddlewareResult = processRoutes(
     routes.beforeMiddleware,
     currentUrl,
-    currentHeaders,
+    currentRequestHeaders,
+    currentResponseHeaders,
     initialOrigin
   )
 
@@ -419,15 +442,15 @@ export async function resolveRoutes(
   if (beforeMiddlewareResult.redirect) {
     return {
       redirect: beforeMiddlewareResult.redirect,
-      resolvedHeaders: currentHeaders,
+      resolvedHeaders: currentResponseHeaders,
       status: currentStatus,
     }
   }
 
   if (beforeMiddlewareResult.externalRewrite) {
     return {
       externalRewrite: beforeMiddlewareResult.externalRewrite,
-      resolvedHeaders: currentHeaders,
+      resolvedHeaders: currentResponseHeaders,
       status: currentStatus,
     }
   }
@@ -442,7 +465,7 @@ export async function resolveRoutes(
   // Invoke middleware
   const middlewareResult = await invokeMiddleware({
     url: currentUrl,
-    headers: currentHeaders,
+    headers: currentRequestHeaders,
     requestBody,
   })
 
@@ -453,14 +476,30 @@ export async function resolveRoutes(
 
   // Apply request headers from middleware
   if (middlewareResult.requestHeaders) {
-    currentHeaders = new Headers(middlewareResult.requestHeaders)
+    currentRequestHeaders = new Headers(middlewareResult.requestHeaders)
+  }
+
+  // Apply response headers from middleware
+  if (middlewareResult.responseHeaders) {
+    middlewareResult.responseHeaders.forEach((value, key) => {
+      if (key.toLowerCase() === 'set-cookie') {
+        currentResponseHeaders.append(key, value)
+      } else {
+        currentResponseHeaders.set(key, value)
+      }
+    })
   }
 
   // Handle middleware redirect
   if (middlewareResult.redirect) {
-    currentHeaders.set('Location', middlewareResult.redirect.url.toString())
+    if (!currentResponseHeaders.has('location')) {
+      currentResponseHeaders.set(
+        'Location',
+        middlewareResult.redirect.url.toString()
+      )
+    }
     return {
-      resolvedHeaders: currentHeaders,
+      resolvedHeaders: currentResponseHeaders,
       status: middlewareResult.redirect.status,
     }
   }
@@ -473,7 +512,7 @@ export async function resolveRoutes(
     if (currentUrl.origin !== initialOrigin) {
       return {
         externalRewrite: currentUrl,
-        resolvedHeaders: currentHeaders,
+        resolvedHeaders: currentResponseHeaders,
         status: currentStatus,
       }
     }
@@ -488,7 +527,8 @@ export async function resolveRoutes(
   const beforeFilesResult = processRoutes(
     routes.beforeFiles,
     currentUrl,
-    currentHeaders,
+    currentRequestHeaders,
+    currentResponseHeaders,
     initialOrigin
   )
 
@@ -499,15 +539,15 @@ export async function resolveRoutes(
   if (beforeFilesResult.redirect) {
     return {
       redirect: beforeFilesResult.redirect,
-      resolvedHeaders: currentHeaders,
+      resolvedHeaders: currentResponseHeaders,
       status: currentStatus,
     }
   }
 
   if (beforeFilesResult.externalRewrite) {
     return {
       externalRewrite: beforeFilesResult.externalRewrite,
-      resolvedHeaders: currentHeaders,
+      resolvedHeaders: currentResponseHeaders,
       status: currentStatus,
     }
   }
@@ -531,18 +571,20 @@ export async function resolveRoutes(
         const hasResult = checkHasConditions(
           route.has,
           currentUrl,
-          currentHeaders
+          currentRequestHeaders
         )
         const missingMatched = checkMissingConditions(
           route.missing,
           currentUrl,
-          currentHeaders
+          currentRequestHeaders
         )
 
         if (hasResult.matched && missingMatched) {
           const finalHeaders = applyOnMatchHeaders(
             routes.onMatch,
-            currentHeaders
+            currentUrl,
+            currentRequestHeaders,
+            currentResponseHeaders
           )
           return {
             matchedPathname: matchedPath,
@@ -555,7 +597,12 @@ export async function resolveRoutes(
     }
 
     // No dynamic route matched, return without route matches
-    const finalHeaders = applyOnMatchHeaders(routes.onMatch, currentHeaders)
+    const finalHeaders = applyOnMatchHeaders(
+      routes.onMatch,
+      currentUrl,
+      currentRequestHeaders,
+      currentResponseHeaders
+    )
     return {
       matchedPathname: matchedPath,
       resolvedHeaders: finalHeaders,
@@ -570,12 +617,12 @@ export async function resolveRoutes(
 
   // Process afterFiles routes
   for (const route of routes.afterFiles) {
-    const match = matchRoute(route, currentUrl, currentHeaders)
+    const match = matchRoute(route, currentUrl, currentRequestHeaders)
 
     if (match.matched) {
-      if (route.headers) {
-        for (const [key, value] of Object.entries(route.headers)) {
-          currentHeaders.set(key, value)
+      if (match.headers) {
+        for (const [key, value] of Object.entries(match.headers)) {
+          currentResponseHeaders.set(key, value)
         }
       }
 
@@ -587,8 +634,8 @@ export async function resolveRoutes(
         // Check if route has redirect status and Location/Refresh header
         if (
           isRedirectStatus(route.status) &&
-          route.headers &&
-          hasRedirectHeaders(route.headers)
+          match.headers &&
+          hasRedirectHeaders(match.headers)
         ) {
           const redirectUrl = isExternalDestination(match.destination)
             ? new URL(match.destination)
@@ -599,7 +646,7 @@ export async function resolveRoutes(
               url: redirectUrl,
               status: route.status!,
             },
-            resolvedHeaders: currentHeaders,
+            resolvedHeaders: currentResponseHeaders,
             status: currentStatus,
           }
         }
@@ -608,7 +655,7 @@ export async function resolveRoutes(
         if (isExternalDestination(match.destination)) {
           return {
             externalRewrite: new URL(match.destination),
-            resolvedHeaders: currentHeaders,
+            resolvedHeaders: currentResponseHeaders,
             status: currentStatus,
           }
         }
@@ -620,7 +667,7 @@ export async function resolveRoutes(
         if (currentUrl.origin !== initialOrigin) {
           return {
             externalRewrite: currentUrl,
-            resolvedHeaders: currentHeaders,
+            resolvedHeaders: currentResponseHeaders,
             status: currentStatus,
           }
         }
@@ -630,7 +677,8 @@ export async function resolveRoutes(
           routes.dynamicRoutes,
           currentUrl,
           pathnames,
-          currentHeaders,
+          currentRequestHeaders,
+          currentResponseHeaders,
           routes.onMatch,
           basePath,
           buildId,
@@ -660,7 +708,9 @@ export async function resolveRoutes(
         if (matchedPath) {
           const finalHeaders = applyOnMatchHeaders(
             routes.onMatch,
-            currentHeaders
+            pathnameCheckUrl,
+            currentRequestHeaders,
+            currentResponseHeaders
           )
           return {
             matchedPathname: matchedPath,
@@ -681,23 +731,31 @@ export async function resolveRoutes(
       const hasResult = checkHasConditions(
         route.has,
         currentUrl,
-        currentHeaders
+        currentRequestHeaders
       )
       const missingMatched = checkMissingConditions(
         route.missing,
         currentUrl,
-        currentHeaders
+        currentRequestHeaders
       )
 
       if (hasResult.matched && missingMatched) {
         // Check if the destination pathname (template path) is in the provided pathnames list
         // For dynamic routes, the destination contains the template path like /dynamic/[slug]
-        const pathnameToCheck = match.destinationPathname || currentUrl.pathname
+        const pathnameToCheck = route.destination
+          ? replaceDestination(
+              route.destination,
+              match.regexMatches || null,
+              hasResult.captures
+            ).split('?')[0]
+          : currentUrl.pathname
         matchedPath = matchesPathname(pathnameToCheck, pathnames)
         if (matchedPath) {
           const finalHeaders = applyOnMatchHeaders(
             routes.onMatch,
-            currentHeaders
+            currentUrl,
+            currentRequestHeaders,
+            currentResponseHeaders
           )
           return {
             matchedPathname: matchedPath,
@@ -712,12 +770,12 @@ export async function resolveRoutes(
 
   // Process fallback routes
   for (const route of routes.fallback) {
-    const match = matchRoute(route, currentUrl, currentHeaders)
+    const match = matchRoute(route, currentUrl, currentRequestHeaders)
 
     if (match.matched) {
-      if (route.headers) {
-        for (const [key, value] of Object.entries(route.headers)) {
-          currentHeaders.set(key, value)
+      if (match.headers) {
+        for (const [key, value] of Object.entries(match.headers)) {
+          currentResponseHeaders.set(key, value)
         }
       }
 
@@ -729,8 +787,8 @@ export async function resolveRoutes(
         // Check if route has redirect status and Location/Refresh header
         if (
           isRedirectStatus(route.status) &&
-          route.headers &&
-          hasRedirectHeaders(route.headers)
+          match.headers &&
+          hasRedirectHeaders(match.headers)
         ) {
           const redirectUrl = isExternalDestination(match.destination)
             ? new URL(match.destination)
@@ -741,7 +799,7 @@ export async function resolveRoutes(
               url: redirectUrl,
               status: route.status!,
             },
-            resolvedHeaders: currentHeaders,
+            resolvedHeaders: currentResponseHeaders,
             status: currentStatus,
           }
         }
@@ -750,7 +808,7 @@ export async function resolveRoutes(
         if (isExternalDestination(match.destination)) {
           return {
             externalRewrite: new URL(match.destination),
-            resolvedHeaders: currentHeaders,
+            resolvedHeaders: currentResponseHeaders,
             status: currentStatus,
           }
         }
@@ -762,7 +820,7 @@ export async function resolveRoutes(
         if (currentUrl.origin !== initialOrigin) {
           return {
             externalRewrite: currentUrl,
-            resolvedHeaders: currentHeaders,
+            resolvedHeaders: currentResponseHeaders,
             status: currentStatus,
           }
         }
@@ -772,7 +830,8 @@ export async function resolveRoutes(
           routes.dynamicRoutes,
           currentUrl,
           pathnames,
-          currentHeaders,
+          currentRequestHeaders,
+          currentResponseHeaders,
           routes.onMatch,
           basePath,
           buildId,
@@ -802,7 +861,9 @@ export async function resolveRoutes(
         if (matchedPath) {
           const finalHeaders = applyOnMatchHeaders(
             routes.onMatch,
-            currentHeaders
+            pathnameCheckUrl,
+            currentRequestHeaders,
+            currentResponseHeaders
           )
           return {
             matchedPathname: matchedPath,
@@ -816,7 +877,7 @@ export async function resolveRoutes(
 
   // No match found
   return {
-    resolvedHeaders: currentHeaders,
+    resolvedHeaders: currentResponseHeaders,
     status: currentStatus,
   }
 }