When Next.js 15.5.5 Middleware Does Not Fire in Pages Router

Hero image for When Next.js 15.5.5 Middleware Does Not Fire in Pages Router. Image by Marcelo Alves.
Hero image for 'When Next.js 15.5.5 Middleware Does Not Fire in Pages Router.' Image by Marcelo Alves.

In Brief

If Middleware appears to be ignored in a Next.js 15.5.5 Pages Router project, check file discovery before blaming the router. In this case, custom pageExtensions meant the framework expected middleware.page.ts, not middleware.ts. Middleware still supported both routers in Next 15; the Next.js 16 rename to Proxy is a separate later migration.

This week I've gone through the slightly arduous process of upgrading all the dependencies on my personal website, so at the time of writing, I'm now running on Next.js 15.5.5, whilst still using Pages Router (simply because I'm not a fan of how overopinionated and prescriptive the static metadata object is in App Router conventions).

Despite following every documented pattern, middleware simply never fires for me and never has in this project. No logs, no redirects, no signs of life whatsoever. This has been the case throughout every subversion of Next 15 so far.

I'm not alone in seeing this symptom either. Developers across GitHub (1, 2, 3), Stack Overflow, and other forums report the same symptom in their own projects. Those reports are useful debugging evidence, but they do not show that Next 15 made Middleware an AppRouteronly feature. The documentation explicitly supports the convention in its version15 Pages Router reference. A silent failure therefore points first to file discovery, naming, matcher, build or deployment configuration.


What Middleware Used to Be

Middleware first arrived as part of Next.js 12, allowing developers to run lightweight logic before a request was processed, essentially in between the frontend and the server. This is really useful for things like detecting locale, redirecting, or authentication.

When deployed, it executes at the Edge runtime, intercepting requests and returning a NextResponse. Initially, it worked perfectly well with the Pages Router. Projects could drop a middleware.ts or middleware.js file at the same level as pages, or inside src when pages also lived there, and gain global preroute logic. It remained supported with the Pages Router in Next 15. Projects with custom pageExtensions had to use the corresponding filename, such as middleware.page.ts.

When Next 13 introduced the app/ directory, Vercel focused many new features and examples on that architecture. That emphasis did not move Middleware into the App Router: it remained a projectlevel request boundary that could run before either Pages or App Router routes.


The Situation in Next 15

In Next 15, the supported model had not changed in that way.

The official version15 Pages Router fileconventions page states that Middleware runs before routes. In this project, the missing detail was the custom pageExtensions configuration: a plain middleware.ts file was outside the accepted filename pattern. With .page.ts configured, the discoverable filename was middleware.page.ts at the same level as src/pages.

The silent result was therefore a filediscovery problem rather than evidence of a Pages Router regression. The community reports may have different causes and should be diagnosed against each project's file placement, pageExtensions, matcher and deployment output.

Interestingly, Next 15.5 introduced stable Node.js Middleware as a headline feature, but that changed the available runtime rather than router support. Edge remained the default and opting into Node.js did not imply that Pages Router hooks had been removed.

Middleware was not deprecated for the Pages Router in Next 15. An absent build entry or a function that never runs should be treated as a configuration or deployment defect until the generated middleware manifest proves otherwise.


The Root Cause and the Checks That Matter

Custom Page Extensions

The project deliberately uses pageExtensions such as page.ts and page.tsx so tests and helpers can sit beside routes. That setting also changes the filename expected for framework entry points. The same convention now used by src/proxy.page.ts was required by src/middleware.page.ts in Next 15.

Location and Static Discovery

Middleware must be statically discovered during build. Put the file at the project root when pages is there, or inside src when using src/pages, and ensure any matcher is a statically analysable constant that includes the request being tested. A missing buildmanifest entry points to discovery; an entry that does not run points to matching or deployment behaviour.

Runtime Constraints

Edge was the default Middleware runtime in Next 15.5, with Node.js available as an explicit option. That choice affects which APIs the function can use; it does not decide whether Pages Router requests pass through Middleware.

These checks follow the version15 documentation. The original failure was real, but the AppRouteronly explanation inferred from it was not.


Practical Workarounds

Start by correcting discovery and verifying the matcher before replacing the architecture. The alternatives below can still be appropriate when the requirement belongs more naturally in configuration or pagelevel server code, but they are not required merely because the project uses Pages Router.

Correcting the File Convention

The direct fix in this project was to use the configured extension: src/middleware.page.ts beside src/pages. A project using the default extensions would use middleware.ts instead. No framework downgrade or router migration was needed.

After renaming the file, clear the .next build cache, restart the development server, and inspect the generated middleware manifest. Test one route included by the matcher before adding application logic.

If the file is discovered locally but not after deployment, compare the build output, framework adapter and deployment logs before changing application architecture. A version rollback can be a diagnostic comparison, but it is not the fix for an invalid filename under custom pageExtensions.

Redirects, rewrites, serverside rendering hooks and API routes remain useful alternatives where they match the requirement. Choose them for their execution boundary, not as compensation for a Pages Router limitation that Next 15 did not have.

Moving to the App Router

Moving to the App Router may suit a wider migration plan, but it was not necessary to make Middleware run in Next 15. The router choice should follow the application's rendering and data requirements rather than this filediscovery fault.

It isn't an immediately straightforward process, but to begin migrating, you would create an app/ directory at your project root and move one or two routes across. You can still keep pages/ in place whilst gradually transitioning. In a mixed application, the correctly named projectlevel Middleware can match requests handled by either router.

Redirects and Rewrites

If you prefer to remain with the Pages Router, many simple middleware use cases such as path rewrites or redirects can be replicated in next.config.js. For example:

export default {  async redirects() {    return [      {        source: '/old-section/:path*',        destination: '/new-section/:path*',        permanent: true,      },    ];  },};

Configurationlevel redirects execute before the request reaches your page, which offers you the same routing control that middleware would provide.

Authentication and Access Control

Serverside logic inside getServerSideProps can replace most authentication checks that would otherwise be handled in middleware:

// pages/dashboard.tsximport type { GetServerSideProps } from 'next';export const getServerSideProps: GetServerSideProps = async ({ req }) => {  const session = req.cookies.session;  if (!session) {    return {      redirect: { destination: '/login', permanent: false },    };  }  return { props: {} };};

Although this is on a pagelevel, it executes reliably, works with all Pages Router routes, and avoids dependency on undocumented or middleware behaviour.

For more complex request preprocessing, use API routes to handle mutations before the main request is served. They can set or adjust headers, manage cookies, and return modified responses, effectively acting as controlled gateways.

Functional Composition

Middleware was always elegant because it encouraged functional composition: a pipeline of small, testable functions operating on requests and producing responses. That same structure can be recreated in your serverside logic or API handlers.

By composing request processors as pure functions, we can maintain clarity and testability even without a formal middleware layer. The result behaves differently but remains conceptually faithful to the same pattern.


Wrapping Up

In Next 15.5, Middleware remained available to Pages Router applications. The failure described here was reproducible, but the explanation was a custom pageExtensions naming mismatch rather than an undocumented removal of router support.

Pages Router developers should first verify the filename, location, matcher and deployment manifest. Configuration, API routes and serverside rendering hooks are alternatives for particular responsibilities; they are not evidence that Middleware belongs exclusively to the App Router.

Key Takeaways

  • Next 15 Middleware supported both Pages Router and App Router requests.
  • Custom pageExtensions also change the required Middleware filename.
  • File location, static discovery, matcher and deployment output should be checked before blaming router architecture.
  • Redirects, API routes and getServerSideProps remain valid alternatives when their execution boundaries fit the requirement.
  • The Next 16 Proxy rename is a later migration and does not explain the Next 15 incident.

Postscript

This article describes a specific issue encountered with Next.js 15.5.5 and the Pages Router. Since publication, Next.js 16 has deprecated Middleware and renamed the file convention to Proxy. Read it as versionspecific debugging context; for current guidance, see my later article Next.js Proxy Replaces Middleware.


Untangling a delivery problem?

Send the symptoms, constraints, and affected routes. I'll help identify whether the issue sits in the application, platform, content model, deployment path, or search surface.