In short
- Next.js 16.0.1 to 16.1.6 let a Server Action through when the request said
Origin: null. Browsers send that value from sandboxed iframes, so a page on another site could make a visitor's browser call your actions with their cookies. - Only the 16.x line is listed: the advisory, the CVE record and NVD all give
>= 16.0.1, < 16.1.7. The bug came in with 16.0.1. The check runs inside Next.js and the advisory excludes no host, so apps on Vercel count too. - Your session cookie decides the real risk. Browsers leave a
SameSite=Laxcookie off a cross-site POST, and Supabase's SSR package and Auth.js set Lax by default.SameSite=None, or no SameSite attribute at all, is the exposed case. - 16.1.7 is the first fix, not the target. The November 2026 security release makes 16.4.5 the current patch for Next.js 16.
- The origin check is a backstop. Each Server Action is a public endpoint and must check the session and the user's right to change the record itself.
On this page
What CVE-2026-27978 is, in plain words
A Server Action is a function marked 'use server' that your page calls when someone submits a form or clicks a button. Next.js turns each call into a POST request to your server. Because a page on another website can also send a POST to your app from a visitor's browser, with the visitor's cookies attached, Server Actions are a target for cross-site request forgery (CSRF): a page the visitor opens makes their browser act on your app as them.
Next.js has two defences, described in its data security guide. Server Actions accept only POST. And Next.js compares the Origin header, which names the site that sent the request, with the Host header (or X-Forwarded-Host), and aborts the action when they differ. CVE-2026-27978 is a hole in that second check.
Browsers send the literal value Origin: null when the real origin is privacy-sensitive or opaque: from a sandboxed iframe, or from a data: or file: page, among other cases listed by MDN. From 16.0.1 to 16.1.6, Next.js read null as "no Origin header", a case it lets through with a warning in the logs, instead of as a foreign origin to refuse. The fix commit puts it plainly: these contexts can still send credentials like cookies.
The bug arrived with 16.0.1. In the 16.0.1 source, a new condition skips the origin check when the header is null. Earlier versions try to parse null as a URL, which throws an error, so the action does not run. That is why 16.0.0 and 15.x sit outside the affected range.
What a stranger can do with it
| Your app on 16.0.1 to 16.1.6 | What a page on another site can do |
|---|---|
Session cookie set with SameSite=None | Run your Server Actions as any signed-in visitor who opens it: change an email address, delete a record, post a message, whatever your actions do. |
| Session cookie with no SameSite attribute | It depends on the browser. Some apply Lax by default; MDN notes that this default still sends the cookie on a POST made within two minutes of the cookie being set. |
Session cookie set with SameSite=Lax or Strict | The browser leaves the cookie off a cross-site POST, so the forged call arrives signed out. Low risk from this CVE. |
| A Server Action that checks no session at all | Anyone can already call it with a plain HTTP request, CVE or not. Fix that first. |
The limit matters as much as the risk. The forged request changes something; the attacker does not get to read your data through it. That is why the advisory scores the confidentiality impact as none and the integrity impact as low.
Severity and exploitation status
GitHub, the CVE numbering authority that issued it for the Next.js maintainers, scores it 5.3, medium, in CVSS 4.0, with the vector AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N: reachable over the network without an account, but the victim has to open the attacker's page, and the impact is limited to changing data. NVD adds its own CVSS 3.1 score of 4.3, medium (AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N). Both say medium; the numbers differ because the two scales differ.
Who is affected
Three conditions together: a vulnerable version, at least one Server Action, and a session cookie the browser sends with a cross-site POST.
| Condition | Exposed | Not exposed |
|---|---|---|
| Next.js version | 16.0.1 to 16.1.6 | 16.1.7 and later. 15.x and older are outside the published range |
| Server Actions | Any 'use server' function your pages call, forms included | An app with no 'use server' function |
| Hosting | Any host: Vercel, a container running next start, the standalone output | The advisory excludes no host |
allowedOrigins | 'null' listed in experimental.serverActions.allowedOrigins: still exposed after upgrading | The default configuration |
| Session cookie | SameSite=None, or no SameSite attribute | SameSite=Lax or Strict |
Two details are worth knowing. Next.js 16.0.0 sits outside the range, but it is exposed to React2Shell, a critical remote code execution flaw fixed in 16.0.7, so it is no reason to stay. And a 16.1.5 or 16.1.6 app, patched for the React Server Components denial of service CVE-2026-23864, is still exposed to this one.
Apps built with AI tools
Supabase's SSR package and Auth.js both set their session cookies with SameSite=Lax by default, so a generated Next.js app that signs users in through either one starts at low risk from this CVE. Do not stop there. A cookie that must work inside a frame on another site, such as an embedded widget, needs SameSite=None, and that changes the answer. So does a custom session cookie set with no SameSite attribute. And if an AI tool wrote your app, do not assume it has no Server Action: run the grep in the checks below.
How to check your Next.js version
Check the installed version, not the range in package.json: "next": "^16.0.0" can still be locked to 16.0.3. At the root of the project:
# npm
npm ls next
# pnpm (add -r in a monorepo)
pnpm why next
# yarn
yarn why next
# the version the Next.js CLI reports
npx next --version# package-lock.json
grep -A1 '"node_modules/next"' package-lock.json
# pnpm-lock.yaml
grep -E '^ /?next@' pnpm-lock.yaml
# yarn.lock (Yarn 1 and Yarn Berry)
grep -A1 -E '^"?next@' yarn.locknext.Then check what production runs: open your live site, open the browser console and type next.version. A patched repository does not help if an old build still serves the traffic.
How to check your live app
A version tells you the flaw is present. These four checks tell you what it reaches.
- List your Server Actions.
grep -rln "use server" app src libprints every file that declares one. Each action your pages use is a public endpoint: Next.js only removes the ones nothing references. - Read your session cookie. Sign in on your live site, open the developer tools, then Application (Chrome) or Storage (Firefox), then Cookies, and read the SameSite column for the session cookie.
- Read your config.
grep -n allowedOrigins next.config.*. If the list contains'null', upgrading alone does not close the hole. - Read each action that changes data. It should load the session and check that this user may change this record, inside the action, before writing anything.
Keybrum tests the flaw by its behaviour on apps whose owner proved ownership. The attack web.nextjs-action-csrf takes a Server Action id from your JavaScript and sends the same call twice: once from a made-up foreign origin, once with Origin: null. If the first is refused and the second passes the check, the bypass is proven. The request body is deliberately undecodable, so your action never runs.
How to fix CVE-2026-27978
Upgrade. The first fixed version is 16.1.7, released on 16 March 2026. It treats null as a real origin, applies the host check to it, and refuses it unless you list 'null' in allowedOrigins.
In November 2026, 16.1.7 is a floor. Later Next.js CVEs, such as the middleware bypass CVE-2026-44575 (fixed in 16.2.5) and the image optimizer SSRF CVE-2026-94483 (fixed in 16.3.8), need later releases. The November 2026 security release, published on 3 November, ships 16.4.5 as the current patch for Next.js 16.
npm install [email protected]
# or
pnpm add [email protected]
# or
yarn add [email protected]If you cannot upgrade today
The advisory lists three workarounds. A fourth, at the proxy, works for apps behind your own reverse proxy.
- Add a CSRF token to sensitive actions: a random value stored with the session, sent in the form, and compared inside the action before it writes anything. A page on another site cannot read it, so it cannot send it.
- Set `SameSite=Strict` on sensitive auth cookies. The browser then sends them only on requests that come from your own site. The cost: a visitor who follows a link to your app from elsewhere arrives signed out on that first page.
- Remove `'null'` from `serverActions.allowedOrigins` unless a flow really needs it and is protected some other way.
- Refuse POST requests whose Origin is `null` at your reverse proxy. Do not filter on the
Next-Actionheader: a Server Action submitted as a plain HTML form does not send it, and the Next.js test added with the fix uses exactly such a form inside a sandboxed iframe.
# In the server block that proxies to Next.js
set $post_from_null "";
if ($http_origin = "null") { set $post_from_null "1"; }
if ($request_method != "POST") { set $post_from_null ""; }
if ($post_from_null = "1") { return 403; }null. Browsers also send null after a redirect across origins, so test sign-in and payment flows that post back to your app on a staging copy first.What to do after upgrading
- 1
Confirm production runs the new version
Type
next.versionin the browser console on your live site. On a self-hosted server, make sure no old container or process still answers. - 2
Test anything that embeds your app
16.1.7 refuses Server Actions from sandboxed iframes by default. If your app runs inside a sandboxed frame on purpose, such as a widget on other sites or a preview, test its forms. Add
'null'toallowedOriginsonly for a flow that needs it, and protect those actions another way, such as a CSRF token, as the advisory asks. - 3
Check the authorization inside each action
The fixed version still lets through a request with no Origin header at all, with a warning. The code comment gives the reason: such a request is hand-built, and a hand-built request carries only the credentials its sender already has. It does mean anyone can call your actions directly with their own account. The origin check stops forged requests; only your code stops a signed-in user from changing someone else's record.
'use server'
import { revalidatePath } from 'next/cache'
import { createClient } from '@/lib/supabase/server'
export async function deleteNote(noteId: string) {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) throw new Error('Not signed in')
// Only a note that belongs to this user
const { error } = await supabase
.from('notes')
.delete()
.eq('id', noteId)
.eq('user_id', user.id)
if (error) throw error
revalidatePath('/notes')
}user_id filter or a row level security policy, any signed-in user could delete any note by its id.That missing owner check is the IDOR vulnerability, and no framework patch fixes it for you. With Supabase, a row level security policy adds the same check in the database. The Next.js security guide covers the other places a Next.js app leaks: route handlers, middleware, environment variables.
Related CVEs: the same release and other Server Action flaws
Next.js 16.1.7 fixed four other CVEs at the same time. The closest is CVE-2026-27977, the same idea on the development server: it lets you block cross-site WebSocket connections to next dev from privacy-sensitive origins. The others are CVE-2026-27979 (a size limit on postponed state that was not always applied), CVE-2026-27980 (fixed by adding a size limit, images.maximumDiskCacheSize, to the next/image disk cache) and CVE-2026-29057 (request smuggling through rewrites).
| CVE | What it does | Link to Server Actions |
|---|---|---|
| CVE-2024-34351 | Server-side request forgery through a hostile Host header | Triggered through a Server Action |
| CVE-2025-55182 (React2Shell) | Remote code execution while decoding React Server Components requests | The same request decoding Server Actions use |
| CVE-2026-23864 | Denial of service in React Server Components | Fixed in 16.1.5, which is still exposed to CVE-2026-27978 |
Timeline
| Date | Event |
|---|---|
| 25 February 2026 | CVE ID reserved by GitHub |
| 16 March 2026 | Next.js 16.1.7 released with the fix, alongside four other CVE fixes |
| 17 March 2026 | Advisory GHSA-mq59-m269-xvcx published; fix commit (pull request 91478) lands in the Next.js repository; CVE record published |
| 18 March 2026 | NVD publishes the CVE; CISA's assessment: exploitation none, not automatable |
| 3 November 2026 | November 2026 security release: 16.4.5 and 15.5.29 become the current patches |
| 6 November 2026 | Still absent from CISA's Known Exploited Vulnerabilities catalog |
Questions
Is Next.js 15 affected by CVE-2026-27978?
No. The advisory, the CVE record and NVD all give the affected range as 16.0.1 up to, but not including, 16.1.7, and the code that let null through first appears in 16.0.1. Next.js 15.5 still needs 15.5.29, from the November 2026 security release, for other CVEs.
Are apps hosted on Vercel affected?
Treat them as affected if they run 16.0.1 to 16.1.6. The origin check runs inside Next.js, and the advisory excludes no hosting platform. Upgrade, commit the lockfile and redeploy.
Why do browsers send Origin: null?
When the real origin is privacy-sensitive or opaque, for example a sandboxed iframe or a page loaded from a data: or file: address. The value is the literal string null. Before 16.1.7, Next.js treated it like a missing header and skipped the check.
Does SameSite=Lax protect me from this Next.js Server Action CSRF?
Mostly. Browsers do not send a SameSite=Lax cookie with a cross-site POST, so a forged call arrives signed out. A cookie with no SameSite attribute is less predictable, and a SameSite=None cookie is fully exposed. Upgrade anyway.
Should I add 'null' to serverActions.allowedOrigins?
Only if a real flow submits Server Actions from a sandboxed frame or another opaque context. The advisory asks you to protect those actions some other way, such as a CSRF token, because listing 'null' reopens exactly this hole.
Is there a public exploit for CVE-2026-27978?
Yes. A third-party tracker, the Exploit Intelligence Platform, lists a public proof of concept on GitHub. The CVE is not in CISA's Known Exploited Vulnerabilities catalog, and we found no report of an attack that used it.
Sources
- 1.GHSA-mq59-m269-xvcx: null origin can bypass Server Actions CSRF checks, Vercel (Next.js) on GitHub
- 2.CVE-2026-27978, NIST National Vulnerability Database
- 3.CVE-2026-27978, CVE Program
- 4.GitHub advisory database: GHSA-mq59-m269-xvcx, GitHub
- 5.Next.js v16.1.7 release notes, Vercel (Next.js) on GitHub
- 6.action-handler.ts at Next.js v16.0.1 (the origin check before the fix), Vercel (Next.js) on GitHub
- 7.Disallow Server Action submissions from privacy-sensitive contexts (fix commit), Vercel (Next.js) on GitHub
- 8.November 2026 Security Release, Next.js Blog
- 9.Data security (Server Actions, allowed origins), Next.js Docs
- 10.serverActions: allowedOrigins, Next.js Docs
- 11.Known Exploited Vulnerabilities Catalog, CISA
- 12.Origin header, MDN Web Docs
- 13.Set-Cookie header: SameSite, MDN Web Docs
- 14.Default cookie options of @supabase/ssr, Supabase on GitHub
- 15.Default cookie options of Auth.js, Auth.js on GitHub
- 16.CVE-2026-27978 exploit tracking, Exploit Intelligence Platform