Next.js ships security on a schedule: what September changed
Next.js shipped two security releases in September. The first came out of band on September 22: a critical remote code execution in the Node.js ImageResponse from next/og, upstream in the SVG renderer Satori, fixed in 16.3.6 with hardening in 15.5.26 (release post). The second was the scheduled kind: announced September 23 with a week of notice, published September 30 as 16.3.8 and 15.5.27, closing exactly seven advisories, one high, five medium, one low (September 2026 Security Release).
I read this cycle from the inside. Until this morning this site ran 16.3.5, inside the affected range, and its social preview images render through the exact component the advisory names: ImageResponse on the Node runtime. The out-of-band patch appeared on September 22. Mine landed fourteen days later, folded into the scheduled release an agent applied here today. Fourteen days at a known critical. The calendar is supposed to shrink that window. It works only if someone acts on the date.
Which of the eight advisories actually hits your config?#
September's total is eight: the Satori critical on the 22nd, seven more on the 30th. Six carry CVE numbers. The Satori critical and one 'use cache' medium exist only as GHSA identifiers. Severity writes the headline. The condition column decides whose problem each one is.
| Advisory | When it fires | What to do |
|---|---|---|
| GHSA-vcvr-r3jv-pc5j, critical, Sept 22 | Node ImageResponse from next/og, 16.2.0 through 16.3.5. Edge and 15.x unaffected | 16.3.6 or later |
| CVE-2026-94483 / GHSA-cjq9-62q9-8jv4, high | SSRF in Image Optimization via an attacker-controlled allow-listed remote URL | Audit images.remotePatterns, then patch |
| CVE-2026-94543 / GHSA-4jqv-mc3x-m676, medium | Cache poisoning of SSG and ISR pages, self-hosted Pages Router only | 16.3.8 or 15.5.27 |
| CVE-2026-94484 / GHSA-mcj8-r9mp-w47p, medium | Root-level catch-all plus SSG or ISR: content substitution, persistent DoS | Same patch, grep the route tree |
| CVE-2026-94485 / GHSA-f87g-xv8r-7p7x, medium | Metadata image routes ignore dynamicParams, webpack builds only | Turbopack or the patch |
| GHSA-h694-7cp9-m8p3, medium | Nested 'use cache' leaks across root param values, Cache Components on | 16.3.8 |
| CVE-2026-94544 / GHSA-3w37-wq28-93x7, medium | Pending 'use cache' fill leaks Draft Mode content into public pages | 16.3.8 |
| CVE-2026-94486 / GHSA-39w2-rjm5-chcv, low | Dev-server MCP endpoint leaks paths and snippets, next dev only | Patch, stop exposing dev servers |
Fine print for the maintenance branch. 15.5.27 carries three of the seven, the two cache poisonings and the metadata image bypass (release notes). The 'use cache' advisories and the dev endpoint do not exist on the 15.5 line. The high SSRF gets the cleanest boundary in the post: "If no images.remotePatterns are configured, your application is not affected."
The schedule is three months old#
The program post landed on July 13. "These were infrequent, but came with no advance notice and caused disruption for our users," the team wrote about its old ad-hoc patches, and moved to monthly advance notices, each naming a date and a maximum severity. The first cycle shipped that July with fixes for 4 high and 5 medium vulnerabilities (program post). August announced for the 26th and published a day early, on the 25th (update post).
September was the third cycle, and the first in which an out-of-band release landed a week ahead of the scheduled one: the Satori fix announced and published the same day, September 22, three hours apart, then the planned release announced September 23 for September 30 (announcement). The week still bent in public. 16.3.7 landed September 29 as a pure bugfix backport, an addendum moved the security content to 16.3.8 and 15.5.27, and the promise shrank: "This release now addresses seven vulnerabilities instead of nine. The remaining two (one critical, one high) are pending upstream coordination and will be addressed in a later Next.js release." As of today, October 6, neither has shipped.
What the patch looked like on a real site#
This site is the Next.js 16 build from the Lighthouse 100 write-up: statically prerendered, exact-pinned, deployed to a VPS by hand with docker compose. The upgrade ran this morning, 16.3.5 to 16.3.8. An agent drove it. The path ships inside the package: a next upgrade command, and docs bundled in node_modules. "Agents always have access to docs that match your installed version, with no network request or external lookup required." The version 16 guide then wants a runtime verification pass.
$ npm install --save-exact next@16.3.8
$ npx next --version
Next.js v16.3.8The rest fits in one sentence: one version line in package.json, the lockfile, 32 tests across 4 files green, the production build clean on the first run, no source file touched. Smoke checks over the homepage, an article page, and both feed formats returned 200. A same-major security patch should be exactly this boring.
Running the table against this repo#
I walked the condition column against this repo. No images.remotePatterns in next.config.ts, so the high SSRF has no surface. No Pages Router and no root-level catch-all, so both cache-poisoning mediums miss. No Cache Components, no Draft Mode, so both 'use cache' advisories miss. Builds run Turbopack by default since Next.js 16, which removes the webpack-only metadata bypass. My catch-all sits under the locale segment. The advisory names root-level. Miss.
The one row that touches this site is the low one: "Only applications run with next dev are affected. Production deployments do not serve this endpoint." It applies to my dev sessions. The deployed site never serves the endpoint.
This advisory endpoint is a different thing from a public MCP route you ship. The site's own /api/mcp runs in production.
None of that makes the table optional reading. This site is self-hosted behind Cloudflare, serves statically generated pages, and ships an opengraph-image route with dynamicParams set to false. Two mediums describe surfaces one refactor away: a root-level catch-all lands one, Cache Components starts the 'use cache' pair. The condition column measures distance, and distance shrinks without an announcement. The SSRF is the same trust-boundary lesson as in my post on agent fetch guards: the request goes wherever a URL matching the allow-list pattern points.
The five-minute config check#
Run it against your repo before the next notice lands:
- Version: npx next --version. 16.3.8 or 15.5.27, or a written reason why not. Netlify's changelog agrees (changelog).
- remotePatterns: if next.config sets images.remotePatterns, the high SSRF is yours until every pattern is audited.
- Pages Router, self-hosted: both cache-poisoning mediums are yours. A root-level catch-all over SSG or ISR is the loudest combination here.
- Cache Components: the Draft leak advisory applies verbatim to 'use cache' plus Draft Mode previews serving draft-dependent content.
- Build tool: webpack plus opengraph-image or twitter-image routes takes the metadata bypass.
- Dev habits: a next dev session your browser reaches from another tab is the whole surface of the low advisory. Keep dev off shared interfaces.
Two of the originally announced nine, one critical and one high, shipped on 14 October in the October out-of-band release, alongside a new critical in the image pipeline. The program promises a monthly advance notice, so the next one has homework waiting. September 30 is behind everyone. The only variable left is the version on your machine. npx next --version.