The patch you applied might not be the one you’re running
Next.js has shipped nine security releases this year, and between October 14 and 21 Next.js, React, Node.js and PHP all shipped security fixes. Here are the ways a patch can look applied when it isn’t, and the checklist I now use on every site I maintain.
Updated October 23, 2026: Amazon has since softened its PHP2Shell wording, and Netlify, Node.js, Vercel and PHP have added detail, so those sections are updated. I also corrected two dates and the count of upstream criticals, and filled in the full 2026 release list.
On October 14, Next.js and React shipped fixes for two critical remote code execution flaws. On October 21, Node.js and PHP followed with security releases of their own, and attackers were already probing for the PHP bugs. I maintain several production sites on Next.js, including this one, so I spent part of that week patching.
The version bump was quick. Making sure the fix had actually landed took longer, because there are several ordinary ways for a patch to look applied when it isn’t. Here’s what I ran into and the checklist I use now.
Nine security releases in ten months
By my count from the Next.js advisories page (opens in a new tab), the project has published 44 security advisories this year: 5 critical, 15 high, 21 moderate and 3 low. They arrived in nine security releases, and every one has version numbers you can check on npm:
Read the releases as a list
- January 26: three advisories, fixed in 16.1.5 and 15.5.10.
- March 16: five moderate advisories, fixed in 16.1.7, and on the 15 line in 15.5.13 and 15.5.14 (March 19).
- April 8: one high advisory, fixed in 16.2.3 and 15.5.15.
- May 6 and 7: 13 advisories across two releases a day apart, ending at 16.2.6 and 15.5.18. Vercel’s changelog (opens in a new tab) listed every 13.x and 14.x version as affected, with no patch for either.
- July 21: nine advisories, fixed in 16.2.11 and 15.5.21.
- August 25: two critical remote code execution flaws, one in AVIF image handling and one on Windows-hosted servers, fixed in 16.3.3 and 15.5.24.
- September 22: a critical flaw in
ImageResponse, thenext/ogimage generator, fixed in 16.3.6. - September 30: seven advisories, fixed in 16.3.8 and 15.5.27.
- October 14: two criticals and a high, fixed in 16.4.1 and 15.5.28.
All five of this year’s criticals allow remote code execution. As of October 22, none of the October Next.js or React advisories had been reported as exploited. I wouldn’t count on that lasting. When React2Shell landed last December, Wiz found that 39% of cloud environments (opens in a new tab) contained vulnerable Next.js or React versions, and exploitation followed quickly.
Rapid7 tracked 43 newly exploited high and critical vulnerabilities in the third quarter of 2026 and found exploitation began three days after disclosure on average (opens in a new tab), with 71% of it inside the first week. Datadog’s State of DevSecOps report puts the median dependency 278 days out of date (opens in a new tab). That’s why I don’t leave a security release for the next sprint.
A security patch is now a minor upgrade
Since August, each critical fix has shipped on the newest minor of each line and nowhere else. If you were on 16.2 in August, there was no 16.2 patch. To stay patched on the 16 line, you went from 16.2.11 to 16.3.3, then 16.3.6, then 16.3.8, then 16.4.1. This site made that last jump, from 16.3.8, purely for the security fix.
A minor upgrade is usually painless, but it can still break things. It can pull new transitive versions into the lockfile, and those can fail lint or tests for reasons unrelated to the fix. Since you’ll be doing this roughly once a month, it’s worth having tests that catch real breakage and a deploy you can roll back quickly.
The line you’re on matters too. Next.js’s support policy (opens in a new tab) keeps each major in Maintenance LTS for two years after its release. Next.js 15 was released on October 21, 2024, so by that rule its maintenance window closed on October 21, 2026. The support page hadn’t been updated when I wrote this and still listed 15 as Maintenance LTS, so treat that as my reading of the policy, not an announcement. Either way, I’d plan as if 15.5.28 could be the last 15.x patch. The policy only promises fixes for unsupported versions “in rare circumstances,” and 13 and 14 got nothing this year.
Four traps
Trap one: your lockfile isn’t your package.json
The October image flaw, CVE-2026-96889 (opens in a new tab), isn’t in Next.js’s own code. It’s in librsvg, which Next.js reaches through sharp, the image library behind next/image. The advisory’s workaround is to make sure the sharp Next.js uses is 0.35.5 or later, and it notes that fresh installs get a fixed version automatically. A project with an existing lockfile may not.
It wasn’t new in October, either. The librsvg CVE was published on September 23, and sharp shipped the fix in 0.35.5 on September 27, with its own advisory (opens in a new tab) three days later. That was two to three weeks before Next.js published on October 14. The ratings differ too: Next.js calls it critical, sharp rates it high.
Next.js lists sharp as an optional dependency with a version range, and the ranges differ by line. On the 16 line, 16.4.1 asks for ^0.35.5, so the upgrade pulls it forward. (16.3.8 asked for ^0.35.4, which allowed the vulnerable release.) On the 15 line, 15.5.28 still accepts ^0.34.3 || ^0.35.5, so a lockfile that already resolved an older sharp can keep it after you bump Next.js. An override in your own package.json takes precedence over either range.
I hit both of these. My studio’s site had an override pinning sharp to 0.35.4. On a client site, the upgraded Next.js still accepted an older sharp. Both looked patched in package.json, but both were still installing a vulnerable sharp.
# What is actually installed, and why
npm ls sharp next react react-dom
npm explain sharp
pnpm why sharp
yarn why sharp
# Overrides that can hold an old version in place
grep -nE '"(overrides|resolutions)"' package.json
grep -n -A5 '"pnpm"' package.jsonThen check what’s deployed. On a page served by Next.js, window.next.version in the browser console reports the version the client bundle was built with. It isn’t proof of what every server function is running, but it catches a common failure: a merged upgrade that never deployed.
The same problem shows up one level higher. I keep this site’s source in one repository and deploy from a lean mirror. The security upgrade went into the mirror first, and a day later the source still said 16.3.8. If I’d synced from it, I would have quietly rolled the live site back, so the source needed the same upgrade.
Trap two: the bug isn’t in Next.js
Four of this year’s five critical Next.js advisories came from code underneath it. Three came through image handling: August’s AVIF flaw through libheif, September’s through Satori, and October’s SVG flaw through librsvg. The fourth, October’s use cache flaw, came from React Server Components. The AVIF and SVG flaws both arrive via sharp. If your app uses next/image with the built-in optimizer, or next/og, watch the image pipeline closely: it produced three of this year’s five criticals.
The advisories are specific about preconditions. The October image flaw applies “when Next.js runs on glibc-based Linux.” Alpine-based Docker images use musl, so they may fall outside that particular advisory. That’s my reading, not Vercel’s statement, and it does nothing for the other October issues. Where you host matters as well. Netlify says its Image CDN serves next/image on its platform, so that path isn’t exercised there (opens in a new tab). The same flaw is still reachable through next/og ImageResponse, which runs in your own functions, and there Netlify puts the impact at a crashed function invocation rather than code execution. The Next.js announcement (opens in a new tab) makes no such statement about Vercel. It just tells you to patch your Next.js dependencies, so that’s what I’d do on any host.
The other two October issues depend on what your app uses:
- The
use cacheflaw affects 16.0.0 through 16.4.0 apps with ause cachefunction that returns multiple typed arrays. There’s no workaround. It comes from an upstream React Server Components flaw (opens in a new tab), fixed inreact-server-dom-*19.0.9, 19.1.10, 19.2.9 and 19.3.1. - The Server Actions denial of service affects App Router apps with at least one Server Action, back to 13.0.0. Pages Router apps, or apps without Server Actions, aren’t affected. There’s no workaround and no patch for 13 or 14.
Use the preconditions to decide which sites to patch first, then patch the rest. The next advisory will likely have different preconditions, and staying on the current minor keeps the fix one small upgrade away.
Trap three: your runtime is someone else’s patch
Node.js shipped 22.23.4, 24.22.1 and 26.12.1 (opens in a new tab) on October 21, fixing ten vulnerabilities. Node rated four of them high and none critical: two TLS hostname bypasses, a DNS name-truncation bug, and an out-of-bounds write in the bundled Brotli decoder that “permits remote code execution.” That last one is the one I’d worry about. It’s an upstream Google Brotli bug, and Google, which assigned the CVE, scores it 9.2. Node 20 reached end of life in April and Node 25 in June, so neither gets these fixes.
Who applies that patch depends on how you run Node. On a managed platform, it’s usually the platform’s job, and its documentation should tell you when the new release reached your functions. Vercel’s bulletin (opens in a new tab), for example, says it added firewall rules the same day and manages patch versions for Node 22 and 24, but projects still on Node 20 have to switch to 22 or 24 and redeploy. In a Docker image, it’s whatever base image you built from. A tag like node:24-alpine only picks up 24.22.1 when you rebuild and redeploy, and it only has it once the image maintainers have published it.
PHP showed how much that lag can matter. In the Hacker News thread (opens in a new tab) on the PHP release, developers described distribution packages and Docker images arriving hours after the fix, while one commenter’s managed host had patched within about an hour. The PHP Foundation (opens in a new tab) says one image vendor broke the embargo more than six hours early. Amazon’s threat intelligence team saw an exploit attempt (opens in a new tab) four days before disclosure and broad exploitation attempts once the fixes shipped, though it said it was unclear whether any would have succeeded. CISA said the same day that it had not observed exploitation (opens in a new tab). So the PHP bugs had been probed but not confirmed exploited, which is still reason enough to patch.
If you also run PHP sites
Many agencies run Next.js for some clients and WordPress for others, so the PHP side is worth a few lines. The fixed releases are 8.5.12, 8.4.27, 8.3.36 and 8.2.35 (opens in a new tab). Branches 7.4 to 8.1 have only minimal source backports from the PHP project, and anything older has no fix. PHP 8.2 itself reaches end of life on December 31, 2026, so if you’re on 8.2, plan the move to a newer branch now. The mistakes I saw developers describe were operational:
- Changing the CLI
php.iniwhile PHP-FPM reads its own. Check withphpinfo()served by FPM, notphp -i. - Applying the stopgap with
ini_set()or.user.ini. PHP says to setsession.upload_progress.enabled=0in php.ini, an FPM pool or server configuration, and to restart every worker. On October 23, PHP added (opens in a new tab) that the setting should stay at 0 until the next regular bugfix release, even after patching, because of a separate issue in the same feature (CVE-2026-115560). Apps that never change the session serialize handler at runtime can leave it on. - Upgrading the package without restarting PHP-FPM, which keeps serving the old code.
- Treating a firewall rule as the fix. Cloudflare deployed rules early, but it also reported attackers changing their payloads (opens in a new tab) and told customers to patch anyway.
For site owners, I wrote a plainer guide on Jardine Studio: Is your website exposed to this month’s security flaws? (opens in a new tab)
Trap four: scanners lag the advisories
Dependabot and other scanners are worth running, but they read advisory databases, and those databases lag. In early September, a developer reported on Hacker News (opens in a new tab) that the critical AVIF advisory from August 25 was live on the Next.js repository but returned a 404 from the GitHub advisory API and OSV, and that their scanners had missed it. OSV lists that advisory (opens in a new tab) as published on September 8, two weeks after the fix, and lists both October 14 criticals as published on October 22. That’s a small sample, not a measured rate, but it’s enough that I don’t rely on scanners alone for criticals.
So I watch the sources directly: the security advisories pages for vercel/next.js and facebook/react on GitHub, the Next.js blog, the Node.js security blog, and the PHP announcements. Next.js posted advance notice on October 8 for the October 14 release, and those six days were enough to plan the work before the fixes landed.
The checklist I use
This is what I ran across 12 JavaScript projects in October. Four were live sites, and all four were patched and live the same day, each as its own change so it could be reviewed and rolled back on its own.
- Inventory. For every project, record the framework and version, where it runs, what provides the runtime, and who’s responsible for patching it.
- Resolve versions. Read the lockfile, not
package.json:next,react,react-dom,react-server-dom-*if present, andsharp. Look for overrides and resolutions. - Match the advisory’s preconditions. App Router or Pages Router? Server Actions?
use cache? The image optimizer on glibc Linux?next/og? A Windows host? Use the answers to decide which projects go first. - Upgrade on a branch. Move to the patched minor, force fixed transitive versions where needed, and regenerate the lockfile.
- Verify. Type check, lint, test and run a production build. Serve it locally and load pages and images. Then confirm the resolved versions again.
- Ship and confirm. Deploy, check the live version, and update every other copy of the code: mirrors, templates and starter repositories.
- Record it. The date, the versions before and after, and anything that held an old version in place, so the next patch starts from a known state.
# Quick precondition checks for the October advisories
grep -rln '"use cache"' app src 2>/dev/null
grep -rln '"use server"' app src 2>/dev/null
grep -rn 'ImageResponse' app src 2>/dev/null
grep -n 'unoptimized' next.config.*If you look after sites for clients
On client sites, the hard part of patching is usually knowing whose job it is. In the PHP thread, the same story came up repeatedly: an agency or freelancer builds the site and moves on, the client assumes the host handles updates, and nobody watches the releases, so when a critical fix lands, nobody applies it.
If you have more than a handful of client sites, the inventory from step one is worth building first. One line per site: what it runs, which versions, where, and whose job the next patch is. When a release lands, you can see which sites are exposed without opening repositories one by one.
Not sure your site is patched?
If you’re not sure whether your site is behind, or you’ve inherited one that nobody has been maintaining, I’ll check it for free. Send me the URL, or for a private project, the package.json and lockfile. I don’t need access to your repository. I’ll reply within one business day with what it runs, which current advisories apply, and what it would take to patch it, whether that’s a quick version bump or a bigger move off Next.js 13, 14 or 15.
If you want me to do the patching, or to keep an eye on releases for you every month, we can talk about that too. Email me at alex@jardinestudio.com, or use the form below. If you’re an agency with client sites in the same position, have a look at how I work with agencies.