The scheduled Next.js security release on November 3, 2026 ships 16.4.5 and 15.5.29 and fixes seven issues. None is a remote code execution bug like the ones patched in October, and every published advisory is rated Moderate. Still, most of them depend on one specific code pattern, so you can work out in a few minutes which ones touch your app. One more thing changes how you check: when we queried the GitHub Advisory Database on November 9, none of the six advisories was in it yet, so npm audit won't report them.
What each of the seven issues needs
The release post lists seven issues and links six GitHub advisories. The advance notice from October 27 called them "seven medium vulnerabilities". The release post labels the first one High, but its advisory says Moderate, CVSS 6.3. The version ranges below come from the advisories themselves.
| Advisory | Affected next versions | Needs |
|---|---|---|
| GHSA-mfr8-8qww-v76h / CVE-2026-119790 | 16.3.0 to 16.4.4 | A use cache function that reads a root param, called both directly and through unstable_cache() |
| GHSA-vc7v-qv4w-wrv8 / CVE-2026-119351 | 16.2.0 to 16.4.4 | Nested use cache functions where the inner one reads a root param |
| GHSA-rhh4-g77r-r9qj / CVE-2026-119349 | 16.0.0 to 16.4.4 | Binary arguments (Uint8Array, Blob) passed to use cache functions |
| Shared shell cache poisoning (no advisory linked) | Not published | Self-hosted next start, Cache Components on, Partial Prefetching off or unset, a specific dynamic route shape |
| GHSA-pc58-hwjq-p385 / CVE-2026-119348 | up to 15.5.28, 16.0.0 to 16.4.4 | Self-hosted, a handler that returns a fetched Response unchanged, behind an HTTP/1.1 proxy that queues pipelined requests |
| GHSA-f3mh-rxg3-f5g2 / CVE-2026-119346 | 15.0.0 to 15.5.28, 16.0.0 to 16.4.4 | Edge runtime routes that set cookies with NextResponse |
| GHSA-7qx7-33qx-rqx2 / CVE-2026-119347 | 15.0.0 to 15.5.28, 16.0.0 to 16.4.4 | Self-hosted image optimization with attacker-controlled image sources |
Two groups fall out of that table. The first four issues sit inside Cache Components, so they only affect 16.x apps that turned it on. The last three reach 15.x as well, and each needs its own feature to be in use.
The cache leaks only affect Cache Components apps
Cache Components became a top-level cacheComponents option in 16.0.0. If your next.config doesn't set it to true, the first four rows don't apply to you. If it does, these are the ones to read closely, because the impact is one user or tenant seeing another's cached data.
Two of the leaks involve root params. These are the dynamic segments above the root layout, such as app/[tenant]/layout.tsx, and you read them through next/root-params, added in 16.3.0. The use cache docs say only the root params a function actually reads become part of its cache key. That's the boundary these bugs break, and multi-tenant and multi-locale apps are exactly the ones that draw it.
The unstable_cache leak (GHSA-mfr8) is a race. It needs the unstable_cache() call to arrive while the function's first cache fill after a process start is still running. It's also the only advisory with a workaround: it states that use cache inside unstable_cache() is unsupported, and that avoiding it is a complete workaround. In practice that combination shows up in half-migrated code, where an old unstable_cache wrapper now calls a function someone converted to use cache. The unstable_cache docs say use cache replaces it for most uses in Next.js 16, so finishing that migration is the fix that outlasts this release.
The binary-argument bug (GHSA-rhh4) is the plainest. Two different Uint8Array or Blob arguments can produce the same cache key, so one caller gets, or poisons, another caller's entry. If you hash, resize or parse uploaded bytes inside a use cache function, you're the target case.
The shared shell issue has no advisory link and no published version range. The release post limits it to self-hosted next start with Partial Prefetching disabled or unset, and notes that configurations with it enabled "were not affected in the reported tests". The Cache Components docs already tell you to set partialPrefetching explicitly. That's worth doing, but upgrading is the fix, not the flag.
The three that also reach 15.x
The cookie bug (GHSA-f3mh) copies a cookie set through NextResponse on an Edge route into an internal x-middleware-set-cookie response header, which exposes HttpOnly cookies such as session tokens. The release post says exploiting it also needs attacker-controlled JavaScript on the same origin, and that Node.js route handlers and cookies().set() aren't affected. It can't overlap with the cache bugs: the runtime config docs mark 'edge' as deprecated and say Cache Components doesn't support the runtime export at all. So it's mostly older code, like a Pages Router login endpoint that still has runtime: 'edge'.
The response-forwarding bug (GHSA-pc58) hits proxy-style route handlers that return fetch(...) without touching the result. The body arrives decompressed but keeps the upstream's compressed framing headers. Behind an HTTP/1.1 reverse proxy that pipelines requests over a shared backend connection, that can mix one client's response into another's. Its 15.x range has no lower bound, so 14.x and older are inside it, and those lines get no patched release.
The SVG bypass (GHSA-7qx7) lets an SVG disguised as AVIF get past the default dangerouslyAllowSVG: false in self-hosted /_next/image. The release post says this raises exposure to SVG decoder bugs but doesn't by itself show XSS or code execution.
Find the patterns in your code
This script reads package-lock.json for every copy of next, reads the Cache Components and Partial Prefetching settings, and greps source files for the pattern behind each advisory. It uses only Node built-ins.
// check-next-nov-2026.mjs: map a Next.js project to the November 3, 2026 advisories
// Usage: node check-next-nov-2026.mjs [project-dir]
import { readFileSync, readdirSync, existsSync } from 'node:fs';
import { join, relative } from 'node:path';
const dir = process.argv[2] ?? '.';
const lock = JSON.parse(readFileSync(join(dir, 'package-lock.json'), 'utf8'));
const parse = (v) => v.split('-')[0].split('.').map(Number);
const lt = (a, b) => {
const [x, y] = [parse(a), parse(b)];
for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] < y[i];
return false;
};
for (const [path, meta] of Object.entries(lock.packages ?? {})) {
if (path !== 'node_modules/next' && !path.endsWith('/node_modules/next')) continue;
const v = meta.version;
const [major] = parse(v);
let status = 'patched';
if (major === 16 && lt(v, '16.4.5')) status = 'VULNERABLE: upgrade to 16.4.5';
else if (major === 15 && lt(v, '15.5.29')) status = 'VULNERABLE: upgrade to 15.5.29';
else if (major < 15) status = 'no fix on this line: move to 15.5.29 or 16.4.5';
console.log(`next ${v} (${path}): ${status}`);
}
const config = ['next.config.ts', 'next.config.mjs', 'next.config.js'].map((f) => join(dir, f)).find(existsSync);
const cfg = config ? readFileSync(config, 'utf8') : '';
const cacheComponents = /cacheComponents\s*:\s*true/.test(cfg);
const pp = cfg.match(/partialPrefetching\s*:\s*(true|false)/)?.[1] ?? 'unset';
console.log(`cacheComponents: ${cacheComponents}, partialPrefetching: ${pp}`);
function* files(d) {
if (!existsSync(d)) return;
for (const e of readdirSync(d, { withFileTypes: true })) {
if (e.name === 'node_modules' || e.name.startsWith('.')) continue;
const p = join(d, e.name);
if (e.isDirectory()) yield* files(p);
else if (/\.(js|jsx|ts|tsx|mjs)$/.test(e.name)) yield p;
}
}
// Each check: a GHSA ID and a test over one file's source.
const checks = [
['GHSA-mfr8 (16.3+)', 'unstable_cache in a Cache Components app', (s) => cacheComponents && s.includes('unstable_cache')],
['GHSA-vc7v (16.2+)', "'use cache' file reads next/root-params", (s) => /['"]use cache/.test(s) && s.includes('next/root-params')],
['GHSA-rhh4 (16.x)', "'use cache' file handles binary data", (s) => /['"]use cache/.test(s) && /\b(Uint8Array|ArrayBuffer|Blob|Buffer)\b/.test(s)],
['GHSA-pc58', 'handler returns a fetch() result directly', (s) => /return\s+(await\s+)?fetch\(/.test(s)],
['GHSA-f3mh', 'Edge runtime route sets a cookie', (s) => /runtime\s*[:=]\s*['"](experimental-)?edge['"]/.test(s) && /\.cookies\.set\(/.test(s)],
];
const roots = ['app', 'pages', 'src', 'lib'].map((r) => join(dir, r));
for (const f of roots.flatMap((r) => [...files(r)])) {
const src = readFileSync(f, 'utf8');
for (const [id, what, test] of checks) if (test(src)) console.log(`${id}: ${what}: ${relative(dir, f)}`);
}
if (cacheComponents && pp !== 'true') console.log('shared-shell issue (no GHSA): Cache Components on, partialPrefetching not true; matters if self-hosted with next start');
if (/dangerouslyAllowSVG\s*:\s*true/.test(cfg)) console.log('GHSA-7qx7: SVG already allowed by config');
else if (/remotePatterns|domains\s*:/.test(cfg)) console.log('GHSA-7qx7: remote image sources configured; matters if self-hosted');
We ran it on Node 23.9.0 against two small sample projects. The first is a multi-tenant 16.x app on 16.4.3 with cacheComponents: true and a lib/data.ts that reads the tenant root param inside use cache, caches thumbnails from a Uint8Array, and still has an unstable_cache wrapper around the settings loader:
next 16.4.3 (node_modules/next): VULNERABLE: upgrade to 16.4.5
cacheComponents: true, partialPrefetching: unset
GHSA-mfr8 (16.3+): unstable_cache in a Cache Components app: lib/data.ts
GHSA-vc7v (16.2+): 'use cache' file reads next/root-params: lib/data.ts
GHSA-rhh4 (16.x): 'use cache' file handles binary data: lib/data.ts
shared-shell issue (no GHSA): Cache Components on, partialPrefetching not true; matters if self-hosted with next start
The second is a 15.x shop on 15.5.28 with remote image patterns, a Pages Router /api/login on the Edge runtime that sets a session cookie, and an App Router avatar proxy that returns fetch() directly:
next 15.5.28 (node_modules/next): VULNERABLE: upgrade to 15.5.29
cacheComponents: false, partialPrefetching: unset
GHSA-pc58: handler returns a fetch() result directly: app/api/avatar/route.ts
GHSA-f3mh: Edge runtime route sets a cookie: pages/api/login.ts
GHSA-7qx7: remote image sources configured; matters if self-hosted
Read every hit as "look at this file", not "you're exploitable". The checks are string matches, and several advisories need more than the script can see. GHSA-vc7v needs one cached function nested inside another. GHSA-mfr8 needs the same function reached both ways. GHSA-pc58 depends on your proxy, which isn't in the repo. A clean run is a better signal than a hit, but upgrading is cheap enough that it shouldn't decide whether you patch.
npm audit is silent for now
The advisories carry CVE IDs this time, but they live on the Next.js repository. npm audit reads the GitHub Advisory Database, and on November 9 the global advisories API returned 404 for all six IDs. The same lookup for October's use cache RCE, GHSA-673q-mjm6-r5xr, returns an entry published on October 22, eight days after its repository advisory. So a clean npm audit this week tells you nothing about this release. Compare the next version in your lockfile against 16.4.5 and 15.5.29 directly.
The upgrade itself is a patch bump: npm install [email protected] or npm install [email protected]. In the published package metadata, 16.4.5 and 15.5.29 keep the same sharp range, React peer range and dependencies as 16.4.4 and 15.5.28, so nothing else in the tree has to move. Rebuild and redeploy, and if you self-host behind a pipelining HTTP/1.1 proxy, put that deploy first.
No comment for this article.