Files
next.js/test/integration/next-image-new/middleware/middleware.js
e9428bba59 fix: middleware not applied to _next/image (#75729)
## What?
This addresses [#68217](https://github.com/vercel/next.js/issues/68217)
where middleware, incorrectly, does not run for _next/image requests.

We can confirm this is not correct behavior through the explanation of
middleware matchers in
[docs](https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher)
> The matcher config allows full regex so matching like negative
lookaheads or character matching is supported. An example of a negative
lookahead to match all except specific paths can be seen here:
> ```typescript
> export const config = {
>   matcher: [
>     /*
>      * Match all request paths except for the ones starting with:
>      * - api (API routes)
>      * - _next/static (static files)
> * - _next/image (image optimization files) <- This means middleware
**should** run for _next/image
>      * - favicon.ico, sitemap.xml, robots.txt (metadata files)
>      */
>
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
>   ],
> }
> ```



### When did this regress
The regression happened here
https://github.com/vercel/next.js/pull/57161/files#diff-6f4291cc2bfc5073fdca12a014011769e840ee68583db1468acef075f037015aL1245

> The diff since github isn't inlining the diff
> ```diff
> - let finished = await this.handleNextDataRequest(req, res, parsedUrl)
> + finished = await this.handle(req, res, parsedUrl)
> ```

> Note, `handle` groups calls to `handleNextDataRequest` **and**
`handleImageRequest`

https://github.com/vercel/next.js/pull/57161/files#diff-6f4291cc2bfc5073fdca12a014011769e840ee68583db1468acef075f037015aR1316-R1328

### Why was this a regression
Inside of `handle`, there is a new call to `handleNextImageRequest`
(this fn handles image optimization).

This ends up causing image optimization to run before user middleware
code runs. When the image optimization completes, the server behaves as
if the entire middleware pipeline is complete, which is **before** user
middleware code is ran


https://github.com/vercel/next.js/blob/d2711d22fb719131d63cbce05a6306917f3626ea/packages/next/src/server/base-server.ts#L1528-L1540

Said plainly, image optimization now runs in middleware. And when image
optimization completes, it blocks user middleware code from running.

Im going to assume this was an incorrect refactor since there did not
seem to be a motivation for the behavior change in
https://github.com/vercel/next.js/pull/57375

### Solution
The solution proposed is to follow the same behavior as
`handleNextDataRequest`, which early returns during middleware:


https://github.com/vercel/next.js/blob/d2711d22fb719131d63cbce05a6306917f3626ea/packages/next/src/server/base-server.ts#L728-L745


> Note, `handleNextDataRequest` has an extra condition that it only
skips middleware requests when its not on edge. I’m going to assume this
is not what we want for image optimization, since there was never an
edge check for image optimization- to hedge I don’t have any knowledge
on what can run on edge in this context


And for confirmation that the new ```handleNextImageRequest``` (added in
the regression mentioned) is truly redundant, you can confirm the image
optimization happens when `invokePath` is specified in the request meta
or when `'x-matched-path'` header exists (as far as my understanding
goes, and based on the behavior I observed from runtime checks, these 2
cases should be exhaustive) :


https://github.com/vercel/next.js/blob/d2711d22fb719131d63cbce05a6306917f3626ea/packages/next/src/server/base-server.ts#L1470-L1526


https://github.com/vercel/next.js/blob/d2711d22fb719131d63cbce05a6306917f3626ea/packages/next/src/server/base-server.ts#L1078-L1378

> what's relevant in both above snippets
> ```typescript
> finished = await this.normalizeAndAttachMetadata(req, res, parsedUrl)
>    if (finished) return 
> ```


> Note: it's probably optimal to not call normalizeAndAttachMetadata in
the if middleware branch at all since both handlers in the function now
no-op if they are handling middleware (which is guaranteed in that
branch). Im not applying that change here to avoid scope of the fix, but
can make the refactor upon request


https://github.com/vercel/next.js/blob/d2711d22fb719131d63cbce05a6306917f3626ea/packages/next/src/server/base-server.ts#L1633-L1646


## TLDR
The problem is that image optimization runs before user middleware code,
and when it completes successfully, it skips running user middleware
code- which means requests to _next/image will not have middleware run
for them. The solution is to not run the image optimization in
middleware that caused the skip

Fixes #68217

---------

Co-authored-by: Joseph Chamochumbi <[email protected]>
2025-02-06 12:39:42 -08:00

11 lines
318 B
JavaScript

import Image from 'next/image'
export async function middleware(request) {
// reference Image so it's not tree shaken / DCE
console.log(`Has image: ${Boolean(Image)}`)
if (request.nextUrl.pathname.startsWith('/_next/image')) {
console.log(`x-_next-image: ${request.nextUrl.searchParams.get('url')}`)
}
}