Developer platforms, tools and supply chain
Next.js: CVE-2026-94482 Server Actions denial of service
Next.js Server Actions check
Crafted requests aimed at a Next.js App Router application that has at least one Server Action can cause excessive CPU consumption and block the processing of further requests in the same Node.js process. No authentication is needed.
| CVE | CVE-2026-94482 (GitHub advisory GHSA-p3m5-59mp-jv5r, CWE-770) |
| Severity | High: 7.5 (CVSS 3), 8.2 (CVSS 4.0) |
| Affected | next 13.0.0 up to 15.5.28, and 16.0.0 up to 16.4.1 |
| Fixed | 15.5.28 and 16.4.1 |
| Move to | 15.5.28 or later: 13.x and 14.x apps upgrade to it |
| Not affected | Pages Router applications, and App Router applications with no Server Action |
Quick start
-
Download the script. The repository is mirrored on GitHub and Codeberg; pick a base URL.
# BASE from GitHub: https://raw.githubusercontent.com/PrivacySafe/security-patches/refs/heads/main # BASE from Codeberg: https://codeberg.org/PrivacySafe/security-patches/raw/branch/main # curl -fLO "$BASE/nextjs/nextjs-server-actions-check.sh" curl -fLO https://raw.githubusercontent.com/PrivacySafe/security-patches/refs/heads/main/nextjs/nextjs-server-actions-check.sh -
Read it before running it, then make it executable.
less nextjs-server-actions-check.sh chmod +x nextjs-server-actions-check.sh -
Check this host. By default it searches
/var/www /srv /opt /home; pass directories to narrow it../nextjs-server-actions-check.sh check ./nextjs-server-actions-check.sh check /srv/web/storefront
Commands
| Command | What it does |
|---|---|
check [DIR ...] (default) |
Reads the Next.js version from package-lock.json, npm-shrinkwrap.json, yarn.lock (classic and Berry), pnpm-lock.yaml and installed node_modules/next/package.json copies. For an affected version it checks whether the project has an app/ or src/app/ directory and a Server Action (a 'use server' directive outside node_modules and .next, or a .next/server/server-reference-manifest.json that lists actions) |
version |
Prints the script version |
Exit codes: 0 nothing exposed, 1 an affected project with Server Actions was found, 2 bad arguments.
How to read the result
- Affected version with Server Actions: exposed. Upgrade, rebuild and restart.
- Affected version, no Server Action found: a warning and exit
0. The advisory defines the exposed population as App Router apps with at least one Server Action, so this project is outside it. A source search can miss built-only deployments, so the script also reads the build manifest, if you ship only a build output and no sources, confirm by reading that manifest. - Pre-release and canary versions are compared by their release number, so a canary built before the fix can look fixed.
- Several versions in one project (a monorepo, or a nested dependency) are each reported.
- Packages that merely contain the name (
next-auth,next-intl) are not matched.
Fixing it
npm install next@15.5.28 # 15.x apps
npm install next@16.4.1 # 16.x apps, or: npm install next@latest
Then rebuild and restart. A running process keeps the old code loaded.
Throttling at the proxy while you upgrade
This slows abuse and does not fix the bug. Server Action calls carry a Next-Action header, so a reverse proxy can rate limit only those requests and leave ordinary page and asset traffic alone. Whether crafted requests always carry that header is not stated in the advisory, so treat this as a speed bump. For nginx:
# in the http block
map $http_next_action $next_action_key {
default "";
"~." $binary_remote_addr; # only requests that carry a Next-Action header
}
limit_req_zone $next_action_key zone=next_actions:10m rate=10r/s;
limit_req_status 429;
# in the server or location that proxies to Next.js
limit_req zone=next_actions burst=20 nodelay;
We checked this on nginx 1.24.0 at a deliberately low rate: requests with a Next-Action header got a short burst of 200 responses and then 429, requests without it were never limited, and the limit recovered after a pause. Pick a rate that suits your real traffic. nginx does not limit requests whose key is empty, which is what makes the header-only behavior work.
Sources
Before you run it
- The fix. Update to 15.5.28 or 16.4.1. Next.js 13.x and 14.x apps move to 15.5.28 or later.
- Only some apps are exposed. Pages Router applications, and App Router applications with no Server Action, are not affected. The script reads the version from
package-lock.json,npm-shrinkwrap.json,yarn.lock,pnpm-lock.yamland installednode_modules/next, then checks for the App Router and for Server Actions ("use server", or a populated.next/server/server-reference-manifest.jsonfor built-only deployments). - There is no hunt. The flaw is a denial of service, and no indicators have been published.
- It also checks running docker and podman containers, unless you name a directory or set
SCAN_CONTAINERS=0. In each running container, a read-only probe lists the project files this check needs: lockfiles,package.json, route directory names, the few source files that contain'use server',config/auth.phpand similar. Only those files are copied into a private temporary folder, and the normal check judges that folder like a host directory. The App Router and Server Action checks work on the copied lockfile, the build manifest, the existence ofapp/orsrc/app/, and the source files that contain'use server', so a container project is judged by the same rules as a host project. A staged symlink is never kept, paths the container reports are validated, and nothing is run in a container beyondfind,grepandtest. The copies are removed on exit. A path shown as.../docker_NAME/PATHis the PATH inside the container NAME. It is skipped when you name directories yourself.
The Security Patches software and this documentation are dedicated to the public domain under CC0 1.0 Universal. Other pages on privacysafe.app/docs are CC BY-SA 4.0 unless the page says otherwise.