SM

Command Palette

Search for a command to run...

Blog

16.3.7 Is Not the Patch: What I Learned Researching the Next.js September Security Release

Syed Moinuddin16 min read
SecurityFrontendReactDev ToolsWorkflow
16.3.7 Is Not the Patch: What I Learned Researching the Next.js September Security Release

The September notice named 16.3.7. It shipped as a bug fix, and the seven fixes landed in 16.3.8. Five days later npm audit still called 16.3.7 clean, and a release cooldown installed it on its own. What applies to your app, and the five places an old Next.js keeps running.

Here is the whole argument up front. The version number in a Next.js security notice is a forecast, and this month it was wrong. The September 23 notice said the fixes would ship in 16.3.7 S1. 16.3.7 shipped on September 29 as a bug fix with none of them, and the seven fixes landed in 16.3.8 and 15.5.27 on September 30, with one critical and one high postponed S1S2. Five days later, three tools I would normally trust still treated 16.3.7 as fine: npm audit reported zero vulnerabilities, a one-day release cooldown installed it on its own, and bumping next in a lockfile left an older image library behind. The only floor worth trusting is the one in the release post, checked against what is actually running.

I started this as a ten-minute patch reminder for a week with three Next.js security posts in it. What changed my mind was running npm audit against a lockfile pinned to 16.3.7 and getting a clean bill of health. The second surprise was worse, because it came from my own advice: the release cooldown I recommended in my keyv worm post is exactly the setting that picks 16.3.7 for you. Everything marked "my test" below I reproduced in a sandbox on October 5, with the configs included so you can rerun them.

Timeline of Next.js 16 security floors from July to October 2026: monthly releases announced Jul 13, 16.2.11 on Jul 21 with 4 high and 5 medium fixes, 16.3.3 on Aug 25 with 2 critical, 16.3.6 out of band on Sep 22 for 1 critical RCE, 16.3.7 on Sep 29 with bug fixes only and newest for 31 hours, 16.3.8 on Sep 30 with seven fixes and 1 critical and 1 high postponed, and npm audit calling 16.3.7 clean on Oct 5.

FIG. 01 · The patched version moved four times in ten weeks · filled dots are floors, the dashed one is 16.3.7 S2S3S4S5S6S7

Why did the safe version keep moving?

Next.js moved to a formal security release program on July 13: advance notice roughly once a month, the highest expected severity in each notice, and time to coordinate firewall rules with hosting providers for apps that are not patched yet S3. The stated reason is volume. The team says LLM-assisted vulnerability research is rising fast, pointing to Mozilla disclosing 271 issues in a single Firefox release, all found by Anthropic's Mythos Preview S3.

The floor on the 16.x line has moved four times since. July's 16.2.11 fixed four high and five medium issues S4. August's 16.3.3 was pulled forward a day after a second critical turned up in an upstream dependency, and it fixed two criticals, including remote code execution through sharp's libheif when optimizing an attacker-controlled AVIF image S5. On September 22, an out-of-band 16.3.6 fixed a critical remote code execution in the Node.js ImageResponse from next/og S6. Then came 16.3.8 S2. August and September only patched 16.3 and 15.5, so anyone still on 16.2 had to take a minor upgrade to get the fixes S5S2.

The npm registry shows how narrow the trap was. 16.3.7 was published at 09:04 UTC on September 29, and 16.3.8 at 16:07 UTC on September 30, so for 31 hours the newest 16.3 release was the one the notice had named, without the fixes S7S1.

What changed between the notice and the release?

Bar chart of September fixes by severity, shipped of announced: critical 0 of 1, high 1 of 2, medium 5 of 5, low 1 of 1. The September 23 notice promised nine fixes in 16.3.7; 16.3.8 and 15.5.27 shipped seven on September 30, with one critical and one high postponed for upstream dependency delays.

FIG. 02 · Nine fixes in 16.3.7 became seven in 16.3.8 · one critical and one high postponed, not cancelled S1S2

The Next.js team did correct the record, with dated updates at the top of the notice S1. The problem is everything that copied the original. A daily.dev summary of the notice still lists 16.3.7 and nine fixes when I checked it on October 5 S8. The careful version of this response looks like Dayopt's ticket from September 29, whose first pre-release step is not to treat 16.3.7 as the fixed version S9. Even that ticket still counts nine advisories, because it was written before the count dropped. This is not new, either. After React2Shell in December 2025, a Vercel Community thread was titled after Next.js docs that suggested the wrong version to patch to S10.

Why does my scanner say 16.3.7 is clean?

Because the scanner has not heard about September yet. On October 5, about five days after disclosure, I ran npm audit with npm 12.2.0 against three lockfiles (my test). With next@16.3.5 it reported one critical, the September 22 next/og advisory. With next@16.3.7 and next@16.3.8 it reported zero vulnerabilities each. The results link to the GitHub Advisory Database, and that is where the gap is: all seven September 30 advisories returned 404 there when I checked, while the September 22 one resolved.

npm audit on October 5, 2026: a lockfile on next 16.3.5 shows 1 critical, while 16.3.7 and 16.3.8 show 0 found, even though all seven September 30 advisories apply to 16.3.7. The September 22 critical took 8 days to reach the GitHub Advisory Database, and the seven September 30 advisories were still missing after 5 days.

FIG. 03 · npm audit said zero on 16.3.7 · the September 22 critical took 8 days to reach the database, the September 30 seven were still missing on October 5 S11S12S13

The lag has a precedent from the same month. The September 22 critical was published in the Next.js repository that day, but the GitHub Advisory Database lists it as published and reviewed on September 30, with a CVSS score of 9.5 S13. For eight days, a lockfile on 16.3.5 would have passed npm audit with that remote code execution in it. The September advisories may be stuck for a mundane reason: the two I opened in the vercel/next.js repository still show their patched versions as "16.3.?" and "15.5.?", and one has no upper bound on its affected range S11S12. I am inferring that the placeholders are why they have not been imported. I cannot see GitHub's review queue.

Which of the seven actually apply to my app?

Not all of them, and where you host changes the answer. Here is the September 30 set, with conditions from the release post and hosting notes from the Next.js and Netlify write-ups S2S14:

AdvisorySeverityYou are exposed ifHosting notes
GHSA-cjq9, SSRF in Image Optimization (CVE-2026-94483)High16.x, with a host in images.remotePatterns you would not trust with its DNS S11Not affected on Netlify S14
GHSA-4jqv, SSG and ISR cache poisoning (CVE-2026-94543)MediumSelf-hosted Pages Router with SSG or ISR pages S2Not affected on Vercel S2
GHSA-mcj8, catch-all cache poisoning (CVE-2026-94484)MediumA root-level catch-all page plus SSG or ISR routes S2An availability issue (HTTP 500) on Netlify S14
GHSA-f87g, metadata image bypass (CVE-2026-94485)MediumApp Router built with webpack, using opengraph-image or twitter-image S2Turbopack builds not affected S2
GHSA-h694, nested use cache leakMediumCache Components, with a nested use cache reading a root param S2Applies to 16.3.x S14
GHSA-3w37, Draft Mode leak (CVE-2026-94544)MediumCache Components plus Draft Mode with draft-dependent cached content S2Not affected on Netlify S14
GHSA-39w2, dev MCP endpoint (CVE-2026-94486)LowRunning next dev on 16.x S2Production never serves it S2

Two things stood out. First, the low-severity one is the only one that reaches every 16.x developer regardless of hosting: next dev exposes a Model Context Protocol endpoint that does not check which website a request comes from, so a malicious page you visit can read your project's path on disk, source snippets from error reports, your route inventory and dev logs S2. Second, the sources disagree on scope for GHSA-4jqv. Netlify's table limits it to apps with a root catch-all route, while the Next.js post and advisory describe any self-hosted Pages Router app with SSG or ISR pages S14S2S12. I would go with the broader reading from the people who wrote the fix.

For the SSRF, the advisory's workaround is worth doing even after you patch: audit images.remotePatterns for any host you would not trust with its own DNS entries S11.

Why did my own cooldown pick 16.3.7?

In the keyv post I suggested a one-day release cooldown, so freshly hijacked packages never reach you. pnpm now ships that by default: since v11, minimumReleaseAge defaults to 1440 minutes, and it only fails loudly when you set it yourself, because an explicit setting turns on strict mode S15. A cooldown cannot tell a worm from a security patch, though, and with a caret range it does something quieter than failing.

I reproduced the moment 16.3.8 shipped by shifting the cooldown so the cutoff fell between the two releases' publish times, then resolved a lockfile with pnpm 12.9.1 (my test).

Timeline at 16:07 UTC on September 30 with a 1440 minute cooldown: 16.3.6 and 16.3.7 are past the cutoff and 16.3.8 is too new, so a ^16.3.6 range resolves to 16.3.7. The exact pin 16.3.8 fails with ERR_PNPM_NO_MATURE_MATCHING_VERSION, exempting next alone fails because 9 @next packages are also too new, and exempting next plus the 9 @next packages at 16.3.8 resolves 16.3.8.

FIG. 04 · A one-day cooldown resolves ^16.3.6 to 16.3.7 · the exact pin fails loudly, the caret range does not S7S15

With "next": "^16.3.6", pnpm resolved next@16.3.7 and exited 0. npm 12.2.0 with min-release-age did the same. With an exact "next": "16.3.8", pnpm stopped with ERR_PNPM_NO_MATURE_MATCHING_VERSION, which is the behavior you want. Exempting only next@16.3.8 also failed, because @next/env and eight @next/swc-* binaries were published minutes earlier and were just as new. What worked was exempting exact versions, which pnpm has supported since v10.19 S15:

# pnpm-workspace.yaml
minimumReleaseAge: 1440
minimumReleaseAgeExcludePrune: true
minimumReleaseAgeExclude:
  # Next.js September 2026 floor. Exact versions only, never whole packages.
  - next@16.3.8
  - '@next/env@16.3.8'
  - '@next/swc-darwin-arm64@16.3.8'
  - '@next/swc-darwin-x64@16.3.8'
  - '@next/swc-linux-arm64-gnu@16.3.8'
  - '@next/swc-linux-arm64-musl@16.3.8'
  - '@next/swc-linux-x64-gnu@16.3.8'
  - '@next/swc-linux-x64-musl@16.3.8'
  - '@next/swc-win32-arm64-msvc@16.3.8'
  - '@next/swc-win32-x64-msvc@16.3.8'

minimumReleaseAgeExcludePrune removes those entries once your lockfile moves past them, according to the docs; I did not test that part S15. Clerk's team does the same bookkeeping in public: when they bumped next for a security fix in May, the pull request also updated the minimum-release-age exclusion in their pnpm-workspace.yaml S16. On npm I only tested the caret case, so the practical check there is that your lockfile actually says 16.3.8 after the bump.

What does bumping next leave behind on a self-hosted box?

This one came from a practitioner. In a pull request merged on October 2, a developer upgrading a self-hosted shop to 16.3.8 pinned sharp at 0.35.4, turned off AVIF in images.formats, and wrote down the risk left open: sharp 0.35.4 bundles libheif 1.23.2, which has open advisories including a critical one S17. Their note says it matters only off Vercel, in Docker or output: standalone, where sharp processes images on your own server S17.

I checked the parts I could. The libheif advisory is real: critical, CVSS 9.8, affecting 1.18.0 through 1.23.2 and fixed in 1.23.3 S18. But the pull request's claim that no stable sharp closes it was already out of date. sharp 0.35.5, published September 27, ships a libvips build with libheif 1.23.5 S19S20. The catch is in the lockfile. When I bumped only next from 16.3.6 to 16.3.8 in an existing lockfile, pnpm kept sharp at 0.35.4, because it still satisfies the ^0.35.4 range next asks for (my test). A fresh resolve picked 0.35.5.

Three lockfile cards: before, next 16.3.6 with sharp 0.35.4, libvips build 1.3.3 and libheif 1.23.2; after bumping only next, next 16.3.8 but sharp still 0.35.4 and libheif still 1.23.2; a fresh resolve gives sharp 0.35.5, libvips build 1.3.4 and libheif 1.23.5. libheif advisory GHSA-x8r2-mggj-j6wr is critical, CVSS 9.8, affecting 1.18.0 to 1.23.2 and fixed in 1.23.3.

FIG. 05 · Bumping next left sharp, and its libheif, where they were · a fresh resolve gets 1.23.5 S17S18S19S20

What I could not establish is whether Next's image optimizer can reach this particular libheif bug after August's AVIF change S5. Treat it as open, and bump sharp with next if you self-host.

How would I check every app this afternoon?

The floors are 16.3.8 on 16.x and 15.5.27 on 15.x S2. 14.x is outside the support policy, so no patches are coming for it S21. I check the artifact rather than the repo, because a merged pull request is not a rebuilt image, and a rebuilt image is not a deploy. This script reads next and sharp from inside each image in a list and compares against the floor. I tested the comparison logic, not your images, and it assumes a Node base image with node on the path:

#!/usr/bin/env bash
# images.txt: one image reference per line, the tags you actually deploy
check() {
  case "$1" in
    16.*) floor=16.3.8 ;;
    15.*) floor=15.5.27 ;;
    none) echo "no next in image"; return ;;
    *)    echo "unsupported line, no patches coming"; return ;;
  esac
  lowest=$(printf '%s\n%s\n' "$1" "$floor" | sort -V | head -n1)
  if [ "$1" != "$floor" ] && [ "$lowest" = "$1" ]; then echo "BELOW FLOOR $floor"; else echo "ok"; fi
}
 
while read -r image; do
  next=$(docker run --rm --entrypoint node "$image" -p "require('next/package.json').version" </dev/null 2>/dev/null || echo none)
  sharp=$(docker run --rm --entrypoint node "$image" -p "require('sharp/package.json').version" </dev/null 2>/dev/null || echo none)
  echo "$image next=$next sharp=$sharp $(check "$next")"
done < images.txt
Five places to check after a Next.js security bump: the repo lockfile, the built image, the running deploy, old public previews that stay vulnerable until deleted, and developer laptops running next dev on 16.x. One floor for all five: 16.3.8 on 16.x and 15.5.27 on 15.x.

FIG. 06 · The lockfile is the first of five places to check · old previews and dev laptops are the two people forget S2S8S14

The last two boxes are the ones that slip. Netlify warns that public deploy previews and branch deploys stay vulnerable until they are deleted, and that its Next.js adapter must reach 5.16.1, which a redeploy picks up unless you pinned it S14. A commenter on the daily.dev thread put the process well: the upgrade ticket should name every deployed Next.js app, reachable previews included, with an owner for each before patch day S8.

What does the side-by-side look like?

SignalWhat it saidWhat was trueWhat to trust instead
The advance notice and its copiesFixes ship in 16.3.7 S1S816.3.7 was a bug fix, fixes in 16.3.8 S1S2The release post, on release day
npm audit on October 50 vulnerabilities on 16.3.7Seven September advisories apply S2The floor, compared yourself
A cooldown with ^16.3.6Install succeededIt resolved 16.3.7An exact pin plus exact-version exemptions S15
Bumping only nextPatchedsharp stayed at 0.35.4, libheif 1.23.2 S18sharp 0.35.5 or later S19
A merged pull requestDoneImages, deploys and previews can lag S14The version read from the artifact

What should you do this afternoon?

If you self-host with Docker or output: standalone:

  • Read next and sharp from every deployed image, not the repo
  • Reach 16.3.8 or 15.5.27, and sharp 0.35.5 or later S2S19
  • Audit images.remotePatterns for hosts you would not trust with DNS S11
  • Treat GHSA-4jqv as yours if you run Pages Router with SSG or ISR S2S12

If you deploy on Vercel or Netlify:

  • Upgrade anyway, since Vercel is listed as unaffected for only one of the seven S2
  • On Netlify, redeploy for adapter 5.16.1 and delete stale public previews S14

Everyone on 16.x:

  • Update every laptop that runs next dev S2
  • Pin the exact floor in security bumps so a cooldown fails loudly
  • Watch for the postponed critical and high, which have no date yet S2
  • Do not close the ticket on a green npm audit this week

What would I do differently next time?

  1. Read the version from the release post, not the notice. The notice is a forecast that the Next.js team updates; copies of it do not update.
  2. Treat a green scanner during disclosure week as a sign the database is behind, not as proof that you are safe. Compare against the floor yourself for at least the first week.
  3. Pair every cooldown with a second rule. My cooldown advice in the keyv post was incomplete: security bumps should pin the exact version and exempt that exact version, so the install fails loudly instead of quietly picking the decoy.

FAQ

  1. Is Next.js 16.3.7 a security release?

    No. It shipped on September 29, 2026 as a bug-fix release. The September security fixes are in 16.3.8 and 15.5.27.

  2. Which versions fix the September 2026 Next.js vulnerabilities?

    16.3.8 on the 16.x line and 15.5.27 on the 15.x line, both released September 30, 2026.

  3. How many vulnerabilities did the September release fix?

    Seven: one high, five medium and one low. The advance notice said nine. One critical and one high were postponed because of upstream dependency delays.

  4. Why does npm audit show 0 vulnerabilities for next 16.3.7?

    On October 5, 2026, the seven September 30 advisories were not yet in the GitHub Advisory Database, which npm audit results link to. The September 22 critical took eight days to appear there.

  5. Am I affected by the image optimization SSRF, CVE-2026-94483?

    Only on 16.x with images.remotePatterns configured. With no remote patterns you are not affected. Netlify says its sites are not affected because image optimization runs on its Image CDN.

  6. Does hosting on Vercel protect me?

    Only partly. The Next.js post lists Vercel as unaffected for just one of the seven, the self-hosted Pages Router cache poisoning issue. Upgrade anyway.

  7. What does the next dev MCP endpoint leak?

    While next dev runs on 16.x, a malicious website you visit can read your project path, source snippets from error reports, your route inventory and development logs. Production builds do not serve the endpoint, and 16.3.8 fixes it.

  8. Why did my release cooldown install 16.3.7?

    With a caret range, pnpm and npm pick the newest version old enough to pass the cooldown. For 24 hours after 16.3.8 shipped, that was 16.3.7. Pin the exact version in security bumps so the install fails loudly instead.

  9. How do I exempt 16.3.8 from pnpm's minimumReleaseAge?

    List exact versions under minimumReleaseAgeExclude: next@16.3.8, plus @next/env and the eight @next/swc binaries at 16.3.8. Exempting next alone still fails.

  10. Should I upgrade sharp too?

    If you self-host, yes. Bumping next in an existing lockfile can leave sharp at 0.35.4, which bundles libheif 1.23.2, inside a critical libheif advisory. sharp 0.35.5 bundles libheif 1.23.5.

Sources

  1. S1Next.js, "Upcoming Next.js September Security Release" (updated September 29 and 30), September 23, 2026, nextjs.org
  2. S2Next.js, "September 2026 Security Release," September 30, 2026, nextjs.org
  3. S3Next.js, "Next.js Security Release and Our Next Patch Release," July 13, 2026, nextjs.org
  4. S4Next.js, "July 2026 Security Release," July 20, 2026, nextjs.org
  5. S5Next.js, "August 2026 Security Release," August 25, 2026, nextjs.org
  6. S6Next.js, "Next.js Security Update for a Critical Upstream Issue," September 22, 2026, nextjs.org
  7. S7npm registry, "next" package metadata and publish times, accessed October 5, 2026, registry.npmjs.org
  8. S8daily.dev, "Upcoming Next.js September Security Release" (summary and comments), September 23, 2026, daily.dev
  9. S9Dayopt, "security(next): 16.3.8 September Security Release," GitHub issue 2970, September 29, 2026, github.com
  10. S10Vercel Community, "Misleading warning about react2shell CVE (suggested wrong Next.js version to patch to)," December 15, 2025, community.vercel.com
  11. S11vercel/next.js, "Server-Side Request Forgery in Image Optimization," GHSA-cjq9-62q9-8jv4, September 30, 2026, github.com
  12. S12vercel/next.js, "Cache poisoning of SSG and ISR pages in self-hosted Next.js applications," GHSA-4jqv-mc3x-m676, September 30, 2026, github.com
  13. S13GitHub Advisory Database, "Next.js: Remote Code Execution in next/og ImageResponse," GHSA-vcvr-r3jv-pc5j, reviewed September 30, 2026, github.com
  14. S14Netlify, "Security Update: Multiple vulnerabilities in Next.js," September 30, 2026, netlify.com
  15. S15pnpm, "Dependency Resolution Settings," accessed October 5, 2026, pnpm.io
  16. S16Clerk, "chore(nextjs): Update next to patched versions for GHSA-26hh-7cqf-hhc6," GitHub PR 8547, May 14, 2026, github.com
  17. S17d-Alvhor, "fix(seguridad): sube Next a 16.3.8 y desactiva AVIF," GitHub PR 12, October 2, 2026, github.com
  18. S18libheif, "Heap buffer overflow in unci mixed-interleave decoding with unequal chroma bit depths," GHSA-x8r2-mggj-j6wr, September 1, 2026, github.com
  19. S19npm registry, "sharp" package metadata, accessed October 5, 2026, registry.npmjs.org
  20. S20npm registry, "@img/sharp-libvips-linux-x64" package metadata and tarballs, accessed October 5, 2026, registry.npmjs.org
  21. S21Next.js, "Support Policy," accessed October 5, 2026, nextjs.org

Written by

Syed Moinuddin

Full Stack Engineer.

Notes on AI tooling, agentic systems, and building things that survive contact with production.

Command Palette

Search for a command to run...