Skip to content

feat(nextjs): Emit the tunnel route handler - #25424

Merged
logaretm merged 18 commits into
developfrom
awad/tunnel-nextjs
Nov 11, 2026
Merged

logaretm merged 18 commits into
developfrom
awad/tunnel-nextjs

Conversation

@logaretm

@logaretm logaretm commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

This overhauls the tunnel implementation of Next.js by doing a few things:

  • Making the implementation use the same logic we have in the other frameworks/core, so it inherits:
    • The randomization is DSN based.
    • Proper forwarding for client IP and several headers.
    • Memory optimizations due to streaming the request/response.
  • We now spit a route file with the route, fully capitalizing on Sigrid's feat(nextjs): Add a tunnel route handler that sends only the event payload to Sentry #25456 this means:
    • No need to have omit for the transport options.

The way it works varies depeding on how the user deploys their application.

On Conventional Servers/Platforms

Our runtime patchers introduced in this stack handle the tunnel request, the user's Next.js app never handles any of these.

On Vercel

We utilize the two-hop idea we did in #25456 by writing a small idempotent app router file. The re-write rules move traffic to the new handler, and the file exports a POST handler for the route.

// Generated by @sentry/nextjs for the managed tunnel on Vercel.
export { POST } from '@sentry/nextjs/tunnel';

Vercel applies certain rules and responds with 404 for tunnel requests that aren't present in re-writes or app router, so our runtime patchers will never trigger. Which is why this workaround was needed.

For everyone else, the runtime patchers will work.

@logaretm
logaretm added this pull request to stack #25201 October 15, 2026 20:01
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

size-limit report 📦

Compared: 02b3743 (PR test merge using develop at 1873c1d)

Against: 1873c1d (develop) · base run #65687

✅ Baseline matches: this diff contains only this PR's changes.

Path Size % Change Change
@sentry/browser 29.98 kB - -
@sentry/browser - with treeshaking flags 28.13 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 28.02 kB - -
@sentry/browser (incl. Tracing) 52.36 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 52.36 kB - -
@sentry/browser (incl. Tracing, Profiling) 55.33 kB - -
@sentry/browser (incl. Tracing, Replay) 92.76 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 81.41 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 97.49 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 110.48 kB - -
@sentry/browser (incl. Feedback) 47.57 kB - -
@sentry/browser (incl. sendFeedback) 35.07 kB - -
@sentry/browser (incl. FeedbackAsync) 40.18 kB - -
@sentry/browser (incl. Metrics) 31.54 kB - -
@sentry/browser (incl. Logs) 31.83 kB - -
@sentry/browser (incl. Metrics & Logs) 32.52 kB - -
@sentry/react 31.84 kB - -
@sentry/react (incl. Tracing) 54.8 kB - -
@sentry/vue 38.39 kB - -
@sentry/vue (incl. Tracing) 55.38 kB - -
@sentry/svelte 30 kB - -
@sentry/remix (Remix 3 client bundle) 57.97 kB - -
CDN Bundle 31.77 kB - -
CDN Bundle (incl. Tracing) 52.94 kB - -
CDN Bundle (incl. Logs, Metrics) 34.58 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 55.45 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 76.05 kB - -
CDN Bundle (incl. Tracing, Replay) 91.22 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 93.77 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 97.34 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 99.93 kB - -
CDN Bundle - uncompressed 92.89 kB - -
CDN Bundle (incl. Tracing) - uncompressed 156.15 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 101.42 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 163.92 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 233.31 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 278.17 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 285.93 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 291.87 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 299.63 kB - -
@sentry/nextjs (client) 57.74 kB -0.08% -44 B 🔽
@sentry/sveltekit (client) 52.88 kB - -
@sentry/core/server 44.51 kB - -
@sentry/core/browser 14.02 kB - -
@sentry/node 157.8 kB +0.02% +16 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 82.31 kB - -
@sentry/node - without tracing 97.23 kB +0.02% +18 B 🔺
@sentry/node - without channel injection 138.13 kB +0.02% +23 B 🔺
@sentry/aws-serverless 106.08 kB +0.03% +23 B 🔺
@sentry/cloudflare (withSentry) - minified 204.45 kB - -
@sentry/cloudflare (withSentry) 508.11 kB - -
@sentry/nextjs/cloudflare (withSentry) - minified 229.45 kB +1.47% +3.32 kB 🔺

View base workflow run

Comment thread packages/nextjs/src/config/types.ts Resolved
@logaretm

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@logaretm

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@logaretm

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@logaretm

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Base automatically changed from awad/tunnel-solidstart to develop October 27, 2026 19:48
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Optional E2E tests

Some optional checks were not successful. They do not block the merge. 4 not successful, 53 successful. See the run.

Check Status
❌ CI: Optional E2E Tests / E2E node-eve Test (optional) Failing after 2m Details
❌ CI: Optional E2E Tests / E2E nextjs-16-cacheComponents (cloudflare) Test (optional) Failing after 10m Details
❌ CI: Optional E2E Tests / E2E node-eve (orchestrion) Test (optional) Failing after 2m Details
❌ CI: Optional E2E Tests / E2E node-eve (latest) Test (optional) Failing after 52s Details
Successful checks
Check Status
✅ CI: Optional E2E Tests / E2E cloudflare-pi-durable Test (optional) Successful in 51s Details
✅ CI: Optional E2E Tests / E2E astro-7 Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (cloudflare, latest) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E gen-ai-libraries Test (optional) Successful in 49s Details
✅ CI: Optional E2E Tests / E2E debug-id-sourcemaps Test (optional) Successful in 39s Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (vinext) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E deno (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E cloudflare-workers-send-to-sentry Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nextjs-14 (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E gen-ai-libraries (cloudflare) Test (optional) Successful in 36s Details
✅ CI: Optional E2E Tests / E2E cloudflare-mastra Test (optional) Successful in 49s Details
✅ CI: Optional E2E Tests / E2E cloudflare-agent Test (optional) Successful in 44s Details
✅ CI: Optional E2E Tests / E2E cloudflare-flue-send-to-sentry Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E cloudflare-pi-durable (latest) Test (optional) Successful in 46s Details
✅ CI: Optional E2E Tests / E2E nuxt-5 (tunnel-dev) Test (optional) Successful in 57s Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (deno) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E node-express-send-to-sentry Test (optional) Successful in 59s Details
✅ CI: Optional E2E Tests / E2E node-openai-decisions Test (optional) Successful in 28s Details
✅ CI: Optional E2E Tests / E2E astro-7-static Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E cloudflare-think Test (optional) Successful in 57s Details
✅ CI: Optional E2E Tests / E2E node-mastra Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E gen-ai-libraries (latest) Test (optional) Successful in 49s Details
✅ CI: Optional E2E Tests / E2E node-mastra (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nestjs-12 (latest) Test (optional) Successful in 41s Details
✅ CI: Optional E2E Tests / E2E nextjs-16-tunnel (cloudflare) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nextjs-orpc Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-flue Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nextjs-16 (vinext, bun) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E node-fastify-6 (tracer provider) Test (optional) Successful in 47s Details
✅ CI: Optional E2E Tests / E2E nextjs-15 (latest) Test (optional) Successful in 4m Details
✅ CI: Optional E2E Tests / E2E nextjs-15 (turbo) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E node-express-mcp-v1 Test (optional) Successful in 28s Details
✅ CI: Optional E2E Tests / E2E node-openai-decisions (latest) Test (optional) Successful in 29s Details
✅ CI: Optional E2E Tests / E2E node-pi-durable (latest) Test (optional) Successful in 35s Details
✅ CI: Optional E2E Tests / E2E node-fastify-6 Test (optional) Successful in 34s Details
✅ CI: Optional E2E Tests / E2E supabase-nextjs Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E tanstackstart-react-sourcemaps Test (optional) Successful in 31s Details
✅ CI: Optional E2E Tests / E2E react-send-to-sentry Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nuxt-5 Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E node-flue (latest) Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nuxt-5-sourcemaps (client source maps kept) Test (optional) Successful in 32s Details
✅ CI: Optional E2E Tests / E2E tanstackstart-react-cloudflare Test (optional) Successful in 37s Details
✅ CI: Optional E2E Tests / E2E react-router-7-framework-instrumentation Test (optional) Successful in 2m Details
✅ CI: Optional E2E Tests / E2E nuxt-5-cloudflare Test (optional) Successful in 27s Details
✅ CI: Optional E2E Tests / E2E vue-3 (no Options API) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E cloudflare-think (ai v6) Test (optional) Successful in 57s Details
✅ CI: Optional E2E Tests / E2E node-pi-durable Test (optional) Successful in 42s Details
✅ CI: Optional E2E Tests / E2E solidstart-2 Test (optional) Successful in 54s Details
✅ CI: Optional E2E Tests / E2E nuxt-5-sourcemaps (static / nuxt generate) Test (optional) Successful in 34s Details
✅ CI: Optional E2E Tests / E2E tanstackstart-react (latest) Test (optional) Successful in 1m Details
✅ CI: Optional E2E Tests / E2E nuxt-5-sourcemaps Test (optional) Successful in 39s Details
✅ CI: Optional E2E Tests / E2E node-express-mcp-v2 Test (optional) Successful in 32s Details
✅ CI: Optional E2E Tests / E2E node-express-send-to-sentry-static Test (optional) Successful in 1m Details

@github-actions

Copy link
Copy Markdown
Contributor

One or more gzipped bundles increased by more than 500 bytes. If this increase is intentional, add the Accept Bundlesize Increase label to this PR to rerun and accept the check.

…equest`

The tunnel route handler now leaves DSN normalization, the allowlist and
tracing suppression to core. As in every other SDK, `allowedDsns` adds to
the server's own DSN, and the default `POST` accepts the DSNs
`serveTunnelIntegration` allows.
…ePath`

`tunnel: true` in the browser now sends to `<basePath>/<hash>`, like the
SvelteKit and React Router SDKs do with their base paths. A `basePath` the
user set and a `tunnel` URL stay as written.
…the server

`withSentryConfig` now injects `basePath` into production server and edge
builds, not only in dev, and the server, edge and Cloudflare inits pass it
to `setManagedTunnelDefaults`. `serveTunnelIntegration` then matches
`/<basePath>/<hash>`, like the browser sends.
Vercel serves unknown paths from its CDN without running the app, so the runtime never sees managed tunnel
requests there. On Vercel builds without `tunnelRoute`, `withSentryConfig` writes the tunnel route handler and
rewrites envelope requests to the DSN-derived paths there. The handler answers only when `serveTunnelIntegration`
is set up, and on a cold start too. Pages Router apps get an `app` directory with only this route.
`tunnel: true` in the browser and `serveTunnelIntegration` on the server serve the tunnel on every Next.js
deployment, so `tunnelRoute` and the `@sentry/nextjs/tunnel` route handlers are deprecated. `withSentryConfig`
warns when `tunnelRoute` is set.
With `trailingSlash: true` Next redirected the browser's POST to the tunnel path without the slash.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

The deprecated `tunnelRoute` now turns on the managed tunnel, like `tunnel: true` with `serveTunnelIntegration()`:
a string is its fixed path, `true` the path derived from the DSN. The proxy rewrite to sentry.io, which forwarded
every header including cookies, and the handler mode rewrite are gone. On Vercel, the generated route also serves the
fixed path.
The route `withSentryConfig` generates on Vercel re-exports it, so it stays.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

…ercel tunnel route

A fixed tunnel `path` set in `Sentry.init` now reaches the route on Vercel too, without `tunnelRoute`. The
envelope content type keeps other requests out, and the handler forwards only envelopes for allowed DSNs.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

…t type

The browser sends `application/x-sentry-envelope` only for a managed tunnel given as `true` or options. A fixed path
injected as a string went out as `text/plain`, which the Vercel rewrite doesn't match.
…erived from the DSN

The Next.js span filters only knew a fixed `tunnelRoute` path, and the `url.path` filters of the managed tunnel don't
match Next.js request spans, which carry only `http.target` at start. A proxy that runs outside of the server, like on
Vercel, sent its spans of tunnel requests. The filters now come from the paths the client serves.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a128721. Configure here.

Comment thread packages/nextjs/test/edgeSdk.test.ts Resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants