Sentry Next.js SDK Middleware Bypass (CVE-2026-114139)
Sentry rates CVE-2026-114139 as Critical, CVSS v4.0 9.1: the @sentry/nextjs SDK skips the application middleware for every path under a configured tunnelRoute on webpack builds, while the tunnel rewrite serves only the exact tunnel path, so an unauthenticated request can reach a route under the tunnel path without middleware authorization. Affected are @sentry/nextjs 9.36.0 to 9.47.1, 10.x before 10.75.2 and 11.0.0 prereleases before 11.0.0-rc.1. Fixed in 9.47.2, 10.75.2 and 11.0.0-rc.1; no exploitation in the wild is known.
TL;DR
- @sentry/nextjs wraps your Next.js middleware so it can skip the middleware for Sentry "tunnel" requests. The skip check was broader than the tunnel itself: every request path under the tunnel path, not just the tunnel endpoint, was skipped.
- Routes that resolve under the tunnel path, for example a direct collision such as
/monitoring/foowithtunnelRoute: '/monitoring', or a root dynamic segment such asapp/[locale]/..., receive those requests without your middleware running. If middleware is the only place those routes are authorized, the authorization is bypassed. - Sentry rates the flaw Critical, CVSS v4.0 9.1, classified as CWE-693, protection mechanism failure. No exploitation in the wild is known as of October 24, 2026, and the issue is not in the CISA KEV catalog. The npm package @sentry/nextjs recorded 7,848,686 downloads in the week of October 16-22, 2026.
- Upgrade to 9.47.2, 10.75.2 or 11.0.0-rc.1. No configuration change is needed. If you cannot upgrade yet, move the tunnelRoute to a path no route of yours can resolve under, build with Turbopack, or enforce authorization in the route itself as well as in middleware.
Am I affected?
You are affected if all of these hold:
- Your app uses @sentry/nextjs in an affected range, with the
tunnelRouteoption set (a string path ortrue). - Your app is built with webpack. Turbopack builds, the default for
next buildfrom Next.js 16, are not affected. - Some route of your application resolves under the tunnel path. That can be a direct collision,
/monitoring/foowithtunnelRoute: '/monitoring', or a dynamic segment at the route root,app/[locale]/..., which matches any first path segment. - Your middleware is the only place that enforces authorization for those routes.
Sentry's advisory also states plainly what the bypass does not do: skipping middleware grants the request no privileges, and routes that check the session server-side are still protected. Sentry's own services and the data in your Sentry organization are not affected.
| Package | Affected versions | Fixed version |
|---|---|---|
| @sentry/nextjs 9.x | 9.36.0 – 9.47.1 | 9.47.2 |
| @sentry/nextjs 10.x | 10.0.0-alpha.0 – 10.75.1 | 10.75.2 |
| @sentry/nextjs 11 prereleases | 11.0.0-alpha.0 – 11.0.0-rc.0 | 11.0.0-rc.1 |
Check what is installed and which conditions apply:
# Installed SDK version
npm ls @sentry/nextjs
node -e "console.log(require('@sentry/nextjs/package.json').version)"
# Lock file check (package-lock.json v2/v3 records the package under "node_modules/@sentry/nextjs")
grep -A1 '"node_modules/@sentry/nextjs"' package-lock.json
# Is the tunnel route configured?
grep -rn "tunnelRoute" next.config.* sentry.* .sentryclirc 2>/dev/null
# Container image check
docker run --rm <your-image> node -e "console.log(require('@sentry/nextjs/package.json').version)"
Then check which routes can resolve under the tunnel path: list your app/ (or pages/) directory segments against the tunnel path's first segment. A root-level dynamic segment such as [locale] matches every first segment.
How do I fix it?
Upgrade the SDK. The skip now matches only the requests the tunnel rewrite actually serves, and no configuration change is needed:
npm install @sentry/nextjs@10.75.2
# 9.x line: npm install @sentry/nextjs@9.47.2
# 11 prerelease line: npm install @sentry/nextjs@11.0.0-rc.1
# or: pnpm add @sentry/nextjs@10.75.2 | yarn add @sentry/nextjs@10.75.2
If you cannot upgrade immediately, Sentry's advisory lists three workarounds:
- Set
tunnelRouteto a path no route of yours can resolve under, or remove the option. - Build with Turbopack, the default for
next buildfrom Next.js 16. - Enforce authorization in the route, layout or data-access path as well as in middleware.
The limits of the workarounds: the first depends on your route structure staying collision-free as the app evolves, the second changes your build pipeline, and the third means duplicating authorization logic that middleware was meant to centralize. The upgrade removes the mismatch inside the SDK for every caller.
What is the root cause?
Next.js middleware runs before routes and is a common place for authorization. When @sentry/nextjs instruments middleware on webpack builds, it wraps your middleware function in wrapMiddlewareWithSentry, which first checks whether the incoming request is a Sentry tunnel request, and if so returns immediately without calling your code.
The tunnel exists to defeat ad blockers. Browser events are sent to a path on your own server, for example /monitoring, and a Next.js rewrite forwards them to Sentry. The rewrite serves only the exact tunnel path with the o and p query parameters set, which carry the Sentry organization and project identifiers.
The skip used a broader test. Before the fix, the wrapper asked only whether the request path sits under the tunnel path:
// packages/nextjs/src/common/utils/tunnelPathnameMatch.ts (before the fix)
export function isPathnameUnderSentryTunnelRoute(pathname: string, tunnelPath: string): boolean {
return pathname === tunnelPath || pathname.startsWith(`${tunnelPath}/`);
}
And the wrapper skipped middleware for anything that matched:
// packages/nextjs/src/common/wrapMiddlewareWithSentry.ts (before the fix)
if (tunnelRoute && typeof tunnelRoute === 'string') {
const req: unknown = args[0];
if (req instanceof Request) {
const url = new URL(req.url);
const isTunnelRequest = isPathnameUnderSentryTunnelRoute(url.pathname, tunnelRoute);
if (isTunnelRequest) {
// returns a response that lets the request continue, without calling
// the wrapped middleware
return new Response(null, { status: 200, headers: { 'x-middleware-next': '1' } });
}
}
}
The gap is the difference between the two checks. The rewrite serves /monitoring with valid o and p parameters. The skip covered /monitoring and every path beginning with /monitoring/, parameters or not. A request to /monitoring/foo is not rewritten to Sentry: Next.js routes it to your application. But the wrapper already returned, so your middleware never runs for it.
In one sentence: the middleware skip answered "is this path under the tunnel route?" when it needed to answer "is this a request the tunnel rewrite will actually serve?", and the difference between those two sets is exactly the set of requests that reach your routes unauthenticated middleware checks.
How is it exploited?
No public proof of concept exists, and no exploitation in the wild is known as of October 24, 2026. The attack path the advisory and the patch describe:
- The target app sets
tunnelRouteto a path, commonly/monitoring, and builds with webpack. - The app has a route that resolves under the tunnel path: either a direct collision, or a root dynamic segment such as
app/[locale]/page.tsx, which matches/monitoringas a locale value. - The attacker sends an unauthenticated request to that route path.
- The SDK's middleware wrapper sees a path under the tunnel route and returns the continue response. The application's middleware body, including its authorization logic, never runs.
- Next.js dispatches the request to the route handler. If nothing between the handler and the data re-checks the session, the request is served as if it had passed middleware.
The exploit is a plain HTTP request. What it achieves depends entirely on what the app's middleware was the sole guard for: authentication redirects, tenant checks, feature gating, or route-level authorization.
Attack flow
/monitoring/foo or /[locale]/..."] --> B["SDK middleware wrapper:
path is under the tunnel route"] B --> C["Wrapper returns continue
response, middleware never runs"] C --> D["Tunnel rewrite does not match:
Next.js routes to the application"] D --> E["Route handler serves the request
without middleware authorization"] E --> F["Impact follows what middleware
was the only guard for"]
The failed check is at B: the skip decision is broader than the rewrite it exists to serve.
What can an attacker do?
Read or act on whatever the affected routes expose without their middleware checks. The advisory scopes the impact to confidentiality and integrity of the vulnerable system, and the CVSS v4.0 vector is AV:N/AC:L/AT:P/PR:N/UI:N, with no privileges and no user interaction. Attack requirements are present because the configuration preconditions must line up: tunnelRoute set, webpack build, a route resolving under the tunnel path, and middleware-only authorization.
What the bypass does not do, per the advisory: it grants no privileges, and routes that check the session server-side stay protected. Sentry's own services and your Sentry organization's data are unaffected.
Why does the fix work?
The fix, pull request #24499 with backports #24565 and #24760, replaces the path-prefix test with a function that matches exactly what the rewrite serves:
// packages/nextjs/src/common/utils/tunnelPathnameMatch.ts (after the fix)
/**
* Returns true only for requests the tunnel rewrite (see `setUpTunnelRewriteRules`) would serve.
*
* This decides whether the user's middleware is skipped, so it must never be broader than the rewrite:
* anything it matches that Next.js does not rewrite to Sentry reaches the app without middleware.
*/
export function isSentryTunnelRequest(request: Request, tunnelPath: string): boolean {
// The SDK transport only ever sends POST requests
if (request.method !== 'POST') {
return false;
}
const url = new URL(request.url);
if (url.pathname !== tunnelPath && url.pathname !== `${tunnelPath}/`) {
return false;
}
// Next.js evaluates `has` conditions against the last value of a repeated query param, so every value has to qualify
return ['o', 'p'].every(key => {
const values = url.searchParams.getAll(key);
return values.length > 0 && values.every(value => /^\d+$/.test(value));
});
}
The wrapper now calls isSentryTunnelRequest(req, tunnelRoute) instead of the prefix test. Three properties make the two sides consistent: only POST requests (the SDK transport never sends anything else), only the exact tunnel path with or without a trailing slash, and only with o and p parameters present and all-digits, mirroring how Next.js evaluates the rewrite's has conditions. Any request that fails one of those checks now goes through your middleware like any other request.
The fix is complete for the described gap: the skip and the rewrite now share one definition of a tunnel request, stated in the code comment as an invariant. The wrapper still exists only on webpack builds; on Turbopack builds it was never active, which is why those builds were never affected.
How do I check if I was compromised?
No indicators of compromise have been published for this advisory, and no exploitation is known. If you run an affected configuration and want to audit for the pattern:
- In web server or Next.js access logs, look for unauthenticated requests that reached routes under the tunnel path, and compare them with what your middleware should have rejected or redirected: requests to
/monitoring/...-style paths carrying nooandpparameters, and requests to root dynamic segments such as/en/...from sources your middleware would have blocked. - Review the affected routes' own server-side handling for anything that assumed middleware had already authenticated the caller.
If you find requests that should have been stopped by middleware and were not, treat those sessions as having reached the route unauthenticated and assess what the route returned.
Timeline
- September 18, 2026: the fix lands on the main branch (commit 878b685, "Align tunnel request matching in middleware with tunnel rewrite").
- September 21, 2026: the v10 backport lands (commit d525a2f); Sentry releases 11.0.0-rc.1 and 10.75.2 on September 22, 2026.
- September 24, 2026: Sentry publishes advisory GHSA-mchm-gjx6-7h55.
- September 28, 2026: the v9 backport lands (commit c122aac); Sentry releases 9.47.2 on September 29, 2026.
- October 21, 2026: CVE-2026-114139 is published by NVD.
- October 22, 2026: the advisory is published and reviewed in the GitHub Advisory Database.
- October 24, 2026: this post. No exploitation in the wild is known; the issue is not in the CISA KEV catalog.
FAQ
Is the default configuration affected?
The tunnel route is opt-in, so the default withSentryConfig without tunnelRoute is not affected. The advisory's conditions are the full checklist: tunnelRoute set, webpack build, a route resolving under the tunnel path, and middleware-only authorization.
Are Turbopack builds affected?
No. The middleware wrapper with the skip exists only in the webpack integration. Turbopack, the default for next build from Next.js 16, never had the skip, which the SDK's own code comments note as a build-consistency problem the fix inherits.
Does this let attackers into my Sentry organization?
No. Sentry's advisory states that Sentry's own services and the data in your Sentry organization are not affected. The bypass is about your application's routes, not Sentry's.
Why is a monitoring SDK rated Critical?
Because the impact follows your middleware, not the SDK. When middleware is the sole authorization boundary for a colliding route, an unauthenticated network request reaches it, which the CVSS v4.0 vector scores at 9.1 with confidentiality and integrity high. Sentry also classifies it as CWE-693, protection mechanism failure.
I use middleware for redirects, not authorization. Should I still upgrade?
Yes. Upgrade when convenient: redirects and other non-authorization middleware uses are not a security impact, but route structures change, and the upgrade carries no breaking changes.
Scan your Next.js applications with Ostorlab to surface the @sentry/nextjs version and tunnelRoute usage across your repositories.
References
- Sentry advisory GHSA-mchm-gjx6-7h55: https://github.com/getsentry/sentry-javascript/security/advisories/GHSA-mchm-gjx6-7h55 (checked October 24, 2026)
- GitHub Advisory Database entry: https://github.com/advisories/GHSA-mchm-gjx6-7h55 (checked October 24, 2026)
- CVE-2026-114139 record (CVSS v4.0 9.1, CWE-693, affected ranges): https://cveawg.mitre.org/api/cve/CVE-2026-114139 (checked October 24, 2026)
- Fix pull request #24499: https://github.com/getsentry/sentry-javascript/pull/24499 (checked October 24, 2026)
- Fix commit 878b685 (main branch): https://github.com/getsentry/sentry-javascript/commit/878b685c85dc12045e47e1932c96f5381d7ac532 (checked October 24, 2026)
- v10 backport commit d525a2f: https://github.com/getsentry/sentry-javascript/commit/d525a2f6015b47384bc139c30d58e0d5435d52e5 (checked October 24, 2026)
- v9 backport commit c122aac: https://github.com/getsentry/sentry-javascript/commit/c122aac4f29541eb6da494296822c91eab3cf5ae (checked October 24, 2026)
- Release 10.75.2 (published September 22, 2026): https://github.com/getsentry/sentry-javascript/releases/tag/10.75.2
- Release 9.47.2 (published September 29, 2026): https://github.com/getsentry/sentry-javascript/releases/tag/9.47.2
- Release 11.0.0-rc.1 (published September 22, 2026): https://github.com/getsentry/sentry-javascript/releases/tag/11.0.0-rc.1
- npm download counts for @sentry/nextjs (7,848,686 downloads, week of October 16-22, 2026): https://api.npmjs.org/downloads/point/last-week/@sentry/nextjs (checked October 24, 2026)