VYPR
Moderate severityNVD Advisory· Published Aug 29, 2025· Updated Sep 2, 2025

Next.js Improper Middleware Redirect Handling Leads to SSRF

CVE-2025-57822

Description

Next.js is a React framework for building full-stack web applications. Prior to versions 14.2.32 and 15.4.7, when next() was used without explicitly passing the request object, it could lead to SSRF in self-hosted applications that incorrectly forwarded user-supplied headers. This vulnerability has been fixed in Next.js versions 14.2.32 and 15.4.7. All users implementing custom middleware logic in self-hosted environments are strongly encouraged to upgrade and verify correct usage of the next() function.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
nextnpm
>= 0.9.9, < 14.2.3214.2.32
nextnpm
>= 15.0.0-canary.0, < 15.4.715.4.7

Affected products

1

Patches

1
9c9aaed5bb93

fix router handling when setting a location response header (#82588)

https://github.com/vercel/next.jsZack TannerAug 13, 2025via ghsa
3 files changed · +68 10
  • packages/next/src/server/lib/router-utils/resolve-routes.ts+33 9 modified
    @@ -18,7 +18,10 @@ import { formatHostname } from '../format-hostname'
     import { toNodeOutgoingHttpHeaders } from '../../web/utils'
     import { isAbortError } from '../../pipe-readable'
     import { getHostname } from '../../../shared/lib/get-hostname'
    -import { getRedirectStatus } from '../../../lib/redirect-status'
    +import {
    +  getRedirectStatus,
    +  allowedStatusCodes,
    +} from '../../../lib/redirect-status'
     import { normalizeRepeatedSlashes } from '../../../shared/lib/utils'
     import { getRelativeURL } from '../../../shared/lib/router/utils/relativize-url'
     import { addPathPrefix } from '../../../shared/lib/router/utils/add-path-prefix'
    @@ -659,15 +662,36 @@ export function getResolveRoutes(
     
                 if (middlewareHeaders['location']) {
                   const value = middlewareHeaders['location'] as string
    -              const rel = getRelativeURL(value, initUrl)
    -              resHeaders['location'] = rel
    -              parsedUrl = url.parse(rel, true)
     
    -              return {
    -                parsedUrl,
    -                resHeaders,
    -                finished: true,
    -                statusCode: middlewareRes.status,
    +              // Only process Location header as a redirect if it has a proper redirect status
    +              // This prevents a Location header with non-redirect status from being treated as a redirect
    +              const isRedirectStatus = allowedStatusCodes.has(
    +                middlewareRes.status
    +              )
    +
    +              if (isRedirectStatus) {
    +                // Process as redirect: update parsedUrl and convert to relative URL
    +                const rel = getRelativeURL(value, initUrl)
    +                resHeaders['location'] = rel
    +                parsedUrl = url.parse(rel, true)
    +
    +                return {
    +                  parsedUrl,
    +                  resHeaders,
    +                  finished: true,
    +                  statusCode: middlewareRes.status,
    +                }
    +              } else {
    +                // Not a redirect: just pass through the Location header
    +                resHeaders['location'] = value
    +
    +                return {
    +                  parsedUrl,
    +                  resHeaders,
    +                  finished: true,
    +                  bodyStream,
    +                  statusCode: middlewareRes.status,
    +                }
                   }
                 }
     
    
  • test/e2e/app-dir/app-middleware/app-middleware.test.ts+24 1 modified
    @@ -6,7 +6,7 @@ import { nextTestSetup, FileRef } from 'e2e-utils'
     import type { Response } from 'node-fetch'
     
     describe('app-dir with middleware', () => {
    -  const { next } = nextTestSetup({
    +  const { next, isNextDeploy } = nextTestSetup({
         files: __dirname,
       })
     
    @@ -248,6 +248,29 @@ describe('app-dir with middleware', () => {
     
         await browser.deleteCookies()
       })
    +
    +  // TODO: This consistently 404s on Vercel deployments. It technically
    +  // doesn't repro the bug we're trying to fix but we need to figure out
    +  // why the handling is different.
    +  if (!isNextDeploy) {
    +    it('should not incorrectly treat a Location header as a rewrite', async () => {
    +      const res = await next.fetch('/test-location-header')
    +
    +      // Should get status 200 (not a redirect status)
    +      expect(res.status).toBe(200)
    +
    +      // Should get the JSON response associated with the route,
    +      // and not follow the redirect
    +      const json = await res.json()
    +      expect(json).toEqual({ foo: 'bar' })
    +
    +      // Ensure the provided location is still on the response
    +      const locationHeader = res.headers.get('location')
    +      expect(locationHeader).toBe(
    +        'https://next-data-api-endpoint.vercel.app/api/random'
    +      )
    +    })
    +  }
     })
     
     describe('app dir - middleware without pages dir', () => {
    
  • test/e2e/app-dir/app-middleware/middleware.js+11 0 modified
    @@ -78,6 +78,17 @@ export async function middleware(request) {
         return res
       }
     
    +  if (request.nextUrl.pathname === '/test-location-header') {
    +    return NextResponse.json(
    +      { foo: 'bar' },
    +      {
    +        headers: {
    +          location: 'https://next-data-api-endpoint.vercel.app/api/random',
    +        },
    +      }
    +    )
    +  }
    +
       return NextResponse.next({
         request: {
           headers: headersFromRequest,
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.