Skip to content

feat(nextjs): Serve the tunnel from the app with a DSN-derived path - #25424

Draft
logaretm wants to merge 93 commits into
awad/tunnel-solidstartfrom
awad/tunnel-nextjs
Draft

logaretm wants to merge 93 commits into
awad/tunnel-solidstartfrom
awad/tunnel-nextjs

Conversation

@logaretm

@logaretm logaretm commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

tunnelRoute rewrote envelopes straight to sentry.io, which leaked the user's cookies and only worked for SaaS DSNs, fixed by answering it from a generated app route on the DSN-derived path.

The unit tests cover the rewrites, the injected options and the route file generation, and e2e variants check the App and Pages routers on Turbopack and webpack.

For reviewers: if you need an overview of what this is, refer to #25199.

closes #25466

@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 📦

Path Size % Change Change
@sentry/browser 30.61 kB +2.46% +734 B 🔺
@sentry/browser - with treeshaking flags 28.57 kB +1.96% +548 B 🔺
@sentry/browser - with treeshaking flags tracing without tracing 28.45 kB +1.9% +529 B 🔺
@sentry/browser (incl. Tracing) 52.84 kB +1.49% +773 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 52.86 kB +1.51% +786 B 🔺
@sentry/browser (incl. Tracing, Profiling) 55.83 kB +1.49% +816 B 🔺
@sentry/browser (incl. Tracing, Replay) 92.55 kB +0.8% +726 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 81.22 kB +0.67% +537 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 97.29 kB +0.77% +739 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 110.25 kB +0.7% +764 B 🔺
@sentry/browser (incl. Feedback) 48.19 kB +1.68% +793 B 🔺
@sentry/browser (incl. sendFeedback) 35.69 kB +2.16% +753 B 🔺
@sentry/browser (incl. FeedbackAsync) 40.82 kB +1.94% +776 B 🔺
@sentry/browser (incl. Metrics) 31.62 kB +2.33% +718 B 🔺
@sentry/browser (incl. Logs) 31.89 kB +2.28% +709 B 🔺
@sentry/browser (incl. Metrics & Logs) 32.56 kB +2.37% +752 B 🔺
@sentry/react 32.41 kB +2.25% +711 B 🔺
@sentry/react (incl. Tracing) 55.1 kB +1.33% +718 B 🔺
@sentry/vue 38.76 kB +1.9% +719 B 🔺
@sentry/vue (incl. Tracing) 55.79 kB +1.42% +781 B 🔺
@sentry/svelte 30.64 kB +2.49% +742 B 🔺
@sentry/remix (Remix 3 client bundle) 57.89 kB +1.42% +807 B 🔺
CDN Bundle 32.16 kB +1.64% +518 B 🔺
CDN Bundle (incl. Tracing) 53.11 kB +1% +524 B 🔺
CDN Bundle (incl. Logs, Metrics) 34.34 kB +1.49% +503 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 55.06 kB +0.99% +538 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 75.31 kB +0.73% +545 B 🔺
CDN Bundle (incl. Tracing, Replay) 90.76 kB +0.57% +508 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 92.69 kB +0.56% +514 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 96.92 kB +0.55% +522 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 98.89 kB +0.56% +547 B 🔺
CDN Bundle - uncompressed 94.5 kB +1.43% +1.32 kB 🔺
CDN Bundle (incl. Tracing) - uncompressed 157.36 kB +0.85% +1.32 kB 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 101.04 kB +1.33% +1.32 kB 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 163.31 kB +0.82% +1.32 kB 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 231.08 kB +0.58% +1.32 kB 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 277.52 kB +0.48% +1.32 kB 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 283.46 kB +0.47% +1.32 kB 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 291.23 kB +0.46% +1.32 kB 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 297.15 kB +0.45% +1.32 kB 🔺
@sentry/nextjs (client) 57.35 kB +0.98% +556 B 🔺
@sentry/sveltekit (client) 53.31 kB +1.64% +859 B 🔺
@sentry/core/server 42.92 kB +4.39% +1.8 kB 🔺
@sentry/core/browser 13.81 kB - -
@sentry/node 153.75 kB +0.44% +668 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 84.04 kB - -
@sentry/node - without tracing 95.53 kB +0.7% +660 B 🔺
@sentry/node - without channel injection 131.78 kB +0.49% +636 B 🔺
@sentry/aws-serverless 103.77 kB +0.71% +730 B 🔺
@sentry/cloudflare (withSentry) - minified 212.6 kB +0.96% +2.01 kB 🔺
@sentry/cloudflare (withSentry) 527.41 kB +0.94% +4.89 kB 🔺
@sentry/nextjs/cloudflare (withSentry) - minified 231.16 kB +1.27% +2.88 kB 🔺

View base workflow run

Comment thread packages/nextjs/src/config/types.ts Outdated
@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.

Comment thread packages/nextjs/src/config/withSentryConfig/tunnel.ts
@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.

Comment thread packages/nextjs/src/config/withSentryConfig/tunnel.ts
Comment thread packages/nextjs/src/common/sentryTunnelRouteHandler.ts
Comment thread packages/nextjs/src/common/utils/dropMiddlewareTunnelRequests.ts
@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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

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 5472088. Configure here.

basePath: userNextConfig.basePath ?? '',
...(path && { path }),
...(allowedDsns && { allowedDsns }),
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Tunnel ignores Next.js trailingSlash

Medium Severity

getManagedTunnelOptions copies basePath from the Next.js config but not trailingSlash, so the browser posts to the slashless DSN path. With trailingSlash: true, Next.js 308s that POST before beforeFiles rewrites, and the DSN rewrite source also omits the optional trailing slash that the fixed-path rewrite includes. Unload-time keepalive fetches often will not follow that redirect, so events never reach the tunnel.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5472088. Configure here.

logaretm added a commit that referenced this pull request Oct 23, 2026
…om the DSN (#25199)

### Overview

SDK Tunnel is a feature that allows client SDKs (e.g: browser) to send
their requests through an endpoint that lives on the user's own
application that forwards to Sentry. This bypasses adblockers and
similar extensions.

Tunnels could be configured as a fixed endpoint defined by the user
(defaulted to `/monitoring`) but that usually ends up in global
adblocker lists. Alternatively, users can tell the SDK to use a random
endpoint to bypass that.

Previously the tunnel logic mostly lived in client bundles and in server
frameworks build time, this had two issues:

- Both client and server bundles needed to share a single ENV during
build, or a single process. So both can agree on the random URL
generated.
- CI/CD workflows means assets can drift, so older client bundles might
be sending requests to older server bundles and vice versa, what happens
then is during the rollout/phase-out, the random endpoints won't match
and thus these envelopes will be getting 404'ed.

This stack of PRs re-works the idea by moving to more stable yet random
URL generation based on the client's DSN and moves the tunnel handling
into the runtime of the core SDK.

Meaning tunneling now is a core SDK capability rather than a meta
framework feature.

Here is how it looks for users:

```ts
// client, posts to a path hashed from the DSN, e.g. `/k3x9q2a`
Sentry.init({ dsn, tunnel: true });

// server, nothing to do in a meta-framework, the SDK answers that path from its request hook.
// You only need this if the browser uses a different DSN than the server.
Sentry.init({ dsn, tunnel: { allowedDsns: [browserDsn] } });
```

And a tunneled request goes like this: the framework hook calls
`handleTunnelRequestIfMatched`, if the path matches it checks the
envelope's DSN against the allowed ones and forwards it to Sentry,
otherwise the request continues as normal.

### This PR

This sets up the core for the new tunnel mechanism that the rest of the
stack will be implemented against, it does the following:

- Sets up the tunnel generation and matching logic.
- Sets up global span/breadcrumb ignore rules for tunneled requests.
- Adds request checking, header forwarding, and the proxying to the
Sentry SaaS DSN.

It also sets up a couple of key decisions that will carry over
throughout the stack:

- **Base paths are respected always:** if a user sets `/app` as a base
path our tunnels handlers will expect that prefix.
- **Servers always allow their own DSNs:** This avoids the footgun where
a user misconfigures `allowedDSNs` on the server, this correction only
applies if the client and server share the same DSN, if there is a
different DSN involved then it is on the user to configure that
correctly. Will work on documenting this.

### Reviewing the stack

This PR and #25200 are where the actual behavior lives so these need the
closest look, mainly `forwardEnvelope` and the base path handling in
`tunnel.ts`.

The framework PRs all follow the same pattern: find the earliest request
hook the framework gives us, call the core handler there, and pass the
framework's base path along. They are small, so ideally whoever knows
that framework best picks it up:

- TanStack Start #25408
- SvelteKit #25415
- Nuxt and Nitro #25418, this one has more going on since it has to run
before the user's own server middleware
- Astro #25419
- Remix #25420
- React Router #25422
- SolidStart #25423

#25414 is tests only, and Next.js #25424 is on hold, I will split it up
before it's ready.

This branch has not been deployed

No deployments
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.

Next.js managed tunnel on the tunnel route handler

1 participant