Skip to content

Commit c8f68d1

Browse files
authored
docs(nextjs): Document the tunnel route handler and tunnel credentials (#20075)
## DESCRIBE YOUR PR Documents the tunnel changes in SDK 11.9.0. ⚠️ Merge this PR only after 10.77.1 and 11.9.0 are released. - Next.js `tunnelRoute`: add handler mode (`app/sentry-tunnel/route.ts`). In this mode, a route handler in the app forwards only the request body to Sentry. Rewrite mode now sends with `credentials: "omit"`. Apps behind Vercel Deployment Protection or another login should use handler mode. - Troubleshooting: list what a custom tunnel must do, add `handleTunnelRequest`, and add a new "Credentials on Tunnel Requests" entry. - Short notes in `options.mdx`, the getting-started includes, the Next.js setup pages, and TanStack Start. Related SDK PRs, all going to be released in **11.9.0**: - getsentry/sentry-javascript#25456 - getsentry/sentry-javascript#25515 - getsentry/sentry-javascript#25509 Backport PRs for **10.77.1**: - getsentry/sentry-javascript#25559 - getsentry/sentry-javascript#25560 - getsentry/sentry-javascript#25561 ## IS YOUR CHANGE URGENT? Help us prioritize incoming PRs by letting us know when the change needs to go live. Select exactly one option. For deadlines, replace `YYYY-MM-DD` with the due date. You can update this information later by editing the PR description. - [ ] Urgent deadline (GA date, etc.): YYYY-MM-DD - [x] Other deadline: with the 11.9.0 release - [ ] No deadline: Not urgent, can wait up to 1 week+ ## SLA - Teamwork makes the dream work, so please add a reviewer to your PRs. - Please give the docs team up to 1 week to review your PR unless you've supplied a deadline. Thanks in advance for your help! ## PRE-MERGE CHECKLIST _Make sure you've checked the following before merging your changes:_ - [ ] Checked Vercel preview for correctness, including links - [ ] PR was reviewed and approved by any necessary SMEs (subject matter experts) - [ ] PR was reviewed and approved by a member of the [Sentry docs team](https://github.com/orgs/getsentry/teams/docs)
1 parent 6519f70 commit c8f68d1

11 files changed

Lines changed: 137 additions & 14 deletions

File tree

‎docs/platforms/javascript/common/configuration/options.mdx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ Environments are case-sensitive. The environment name can't contain newlines, sp
8787

8888
Sets the URL that will be used to transport captured events. This can be used to work around ad-blockers or to have more granular control over events sent to Sentry. Adding your DSN is still required when using this option so necessary attributes can be set on the generated Sentry data. This option **requires the implementation** of a custom server endpoint. Learn more and find examples in [Dealing with Ad-Blockers](/platforms/javascript/troubleshooting/#dealing-with-ad-blockers).
8989

90+
The endpoint must forward only the request body to Sentry and accept only your own DSNs. Learn more in [Using the `tunnel` Option](/platforms/javascript/troubleshooting/#using-the-tunnel-option).
91+
9092
</SdkOption>
9193

9294
<PlatformSection supported={['javascript.aws-lambda']}>

‎docs/platforms/javascript/common/troubleshooting/index.mdx‎

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,28 @@ Sentry.init({
250250
});
251251
```
252252

253-
Once configured, all events will be sent to the `/tunnel` endpoint. This solution, however, requires an additional configuration on the server, as the events now need to be parsed and redirected to Sentry. Here's an example for your server component:
253+
Once configured, all events will be sent to the `/tunnel` endpoint. This solution, however, requires an additional configuration on the server, as the events now need to be parsed and redirected to Sentry.
254+
255+
Your tunnel endpoint must:
256+
257+
- Forward only the request body to Sentry, without the request headers.
258+
- Accept only envelopes whose DSN is on a list of allowed DSNs. Otherwise, anyone can use your endpoint to send requests to other servers.
259+
- Send the envelope only to the host of that DSN.
260+
261+
On a JavaScript server with Fetch API `Request` and `Response` objects, `handleTunnelRequest` from `@sentry/core` (SDK version `10.40.0` and later) does this for you. Starting with versions `10.77.1` (v10) and `11.9.0` (v11), it also rejects requests larger than 20 MiB.
262+
263+
```javascript
264+
import { handleTunnelRequest } from "@sentry/core";
265+
266+
export async function POST(request) {
267+
return handleTunnelRequest({
268+
request,
269+
allowedDsns: ["___PUBLIC_DSN___"],
270+
});
271+
}
272+
```
273+
274+
If you implement the endpoint yourself, here's an example for your server component:
254275

255276
```csharp
256277

@@ -334,7 +355,7 @@ const SENTRY_PROJECT_IDS = ["123456"];
334355

335356
export const postAction = async ({ request }) => {
336357
try {
337-
const envelopeBytes = await req.arrayBuffer();
358+
const envelopeBytes = await request.arrayBuffer();
338359
const envelope = new TextDecoder().decode(envelopeBytes);
339360
const piece = envelope.split("\n")[0];
340361
const header = JSON.parse(piece);
@@ -420,6 +441,32 @@ If you would like to copy and paste the snippet directly, here it is minified:
420441
421442
</Expandable>
422443
444+
<Expandable permalink title="Credentials on Tunnel Requests">
445+
446+
The browser SDK doesn't set the [`credentials`](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) option of `fetch`. With the default value `same-origin`, the browser sends credentials (cookies and HTTP authentication) to a tunnel on the same origin as your app. A tunnel that forwards request headers sends these credentials to Sentry.
447+
448+
<PlatformSection supported={["javascript.nextjs"]}>
449+
450+
The Next.js `tunnelRoute` option handles this for you. See <PlatformLink to="/configuration/build/#tunnelRoute">`tunnelRoute`</PlatformLink>.
451+
452+
</PlatformSection>
453+
454+
Forward only the request body, or tell the browser to send no credentials:
455+
456+
```javascript
457+
Sentry.init({
458+
dsn: "___PUBLIC_DSN___",
459+
tunnel: "/tunnel",
460+
transportOptions: {
461+
fetchOptions: { credentials: "omit" },
462+
},
463+
});
464+
```
465+
466+
If a layer in front of your tunnel requires credentials (for example, Vercel Deployment Protection, Cloudflare Access, or HTTP Basic authentication), it rejects requests without credentials. Keep the default in this case, and make sure that your tunnel doesn't forward the request headers.
467+
468+
</Expandable>
469+
423470
</PlatformCategorySection>
424471
425472
<Expandable permalink title="Third party promise libraries">

‎docs/platforms/javascript/guides/cordova/configuration/options.mdx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ Environments are case-sensitive. The environment name can't contain newlines, sp
5151

5252
Sets the URL that will be used to transport captured events. This can be used to work around ad-blockers or to have more granular control over events sent to Sentry. Adding your DSN is still required when using this option so necessary attributes can be set on the generated Sentry data. This option **requires the implementation** of a custom server endpoint. Learn more and find examples in [Dealing with Ad-Blockers](/platforms/javascript/troubleshooting/#dealing-with-ad-blockers).
5353

54+
The endpoint must forward only the request body to Sentry and accept only your own DSNs. Learn more in [Using the `tunnel` Option](/platforms/javascript/troubleshooting/#using-the-tunnel-option).
55+
5456
</SdkOption>
5557

5658
<SdkOption name="maxBreadcrumbs" type='number' defaultValue="100">

‎docs/platforms/javascript/guides/nextjs/configuration/build/index.mdx‎

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ Note: Enabling this option can lead to longer build times.
205205

206206
<SdkOption name="tunnelRoute" type="string | boolean">
207207
<Alert level="info">
208-
This feature requires **Next.js version 11+** and doesn't currently work with self-hosted Sentry instances.
208+
This feature requires **Next.js version 11+**. Rewrite mode doesn't work with self-hosted Sentry instances.
209209
</Alert>
210210

211211
Tunnel Sentry requests through this route on your Next.js server to prevent ad-blockers from blocking Sentry events from being sent.
@@ -215,15 +215,73 @@ This option can be set to:
215215
- `true` for auto-generated routes, which are unpredictable and change with each deployment.
216216
- A custom static string path like `/error-monitoring`.
217217

218+
<Expandable level="warning" title="If your middleware blocks requests without a session">
219+
220+
Your middleware (`proxy.ts` or `middleware.ts`) also runs for tunnel requests. On v10, the SDK skips your middleware only in rewrite mode with Webpack. Exclude the tunnel route in its `matcher`. This needs a fixed string, because `true` creates a new path for each build:
221+
222+
```typescript {filename:proxy.ts}
223+
export const config = {
224+
matcher: ["/((?!error-monitoring|_next/static|_next/image|favicon.ico).*)"],
225+
};
226+
```
227+
228+
</Expandable>
229+
218230
Learn more about tunneling in the <PlatformLink to="/troubleshooting/#dealing-with-ad-blockers">troubleshooting section</PlatformLink>.
219231

220-
<Expandable level="warning" title="Using Next.js middleware on Turbopack">
221-
If you're using Turbopack, client-side event recording will fail if your
222-
Next.js middleware intercepts the configured tunnel route. To fix this, set
223-
the route to a fixed string (like `/error-monitoring`) and add a negative
224-
matcher like `(?!error-monitoring)` in your middleware to exclude the tunnel
225-
route. If you're not using Turbopack, Sentry will automatically skip the
226-
tunnel route in your middleware.
232+
The tunnel route works in one of two modes:
233+
234+
- **Rewrite mode** (default): A Next.js rewrite proxies each request to Sentry with all its request headers. Works only with Sentry SaaS DSNs.
235+
- **Handler mode**: A route handler in your app forwards only the request body to Sentry. Works with any DSN.
236+
237+
Starting with versions `10.77.1` (v10) and `11.9.0` (v11), the browser sends tunnel requests in rewrite mode [with `credentials: "omit"`](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials).
238+
If a layer in front of your app requires credentials (for example, Vercel Deployment Protection or HTTP Basic authentication), it rejects these requests. Use handler mode in this case.
239+
You can also set `credentials: "same-origin"`, but rewrite mode then forwards the credentials to Sentry:
240+
241+
```javascript {filename:instrumentation-client.ts}
242+
Sentry.init({
243+
dsn: "___PUBLIC_DSN___",
244+
transportOptions: {
245+
fetchOptions: { credentials: "same-origin" },
246+
},
247+
});
248+
```
249+
250+
**Handler mode**
251+
252+
<AvailableSince version="10.77.1 (v10) and v11.9.0 (v11)" />
253+
254+
Keep `tunnelRoute` and add this file. If your app uses a `src` directory, add it at `src/app/sentry-tunnel/route.ts`:
255+
256+
```typescript {filename:app/sentry-tunnel/route.ts}
257+
export { POST } from "@sentry/nextjs/tunnel";
258+
```
259+
260+
This works with any `tunnelRoute` value, including `true`. When the SDK finds the file, the build prints this line:
261+
262+
```bash
263+
[@sentry/nextjs] The tunnel route sends events through app/sentry-tunnel/route.ts. Only the event payload goes to Sentry.
264+
```
265+
266+
If the line is missing, the SDK uses rewrite mode.
267+
268+
The handler accepts only the DSN of your server-side SDK. It rejects other DSNs with a `403` and requests larger than 20 MiB with a `413`. If your browser SDK uses a different DSN, list the allowed DSNs:
269+
270+
```typescript {filename:app/sentry-tunnel/route.ts}
271+
import { createSentryTunnelRoute } from "@sentry/nextjs/tunnel";
272+
273+
export const { POST } = createSentryTunnelRoute({
274+
allowedDsns: ["___PUBLIC_DSN___"],
275+
});
276+
```
277+
278+
<Expandable title="Limits of handler mode">
279+
280+
- Handler mode works only with the App Router.
281+
- The SDK doesn't find the file in a route group, such as `app/(group)/sentry-tunnel/`.
282+
- With `"moduleResolution": "node"` in `tsconfig.json`, the type check fails with `Cannot find module '@sentry/nextjs/tunnel'`. Set `moduleResolution` to `bundler`, `node16`, or `nodenext`.
283+
- Each event request runs the route handler. On serverless hosts, this is one function invocation for each request.
284+
227285
</Expandable>
228286

229287
</SdkOption>

‎docs/platforms/javascript/guides/nextjs/index.mdx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ If you use the App Router, the wizard adds Sentry to `app/global-error.tsx` to c
139139

140140
The wizard wraps your Next.js configuration with `withSentryConfig`. This instruments your app at build time, uploads source maps, and, if you said yes in the wizard, routes browser requests to Sentry through your server to avoid ad blockers.
141141

142+
To forward only the request body to Sentry, add a tunnel route handler (SDK version `10.77.1` and later on v10, `11.9.0` and later on v11). Use the handler if Vercel Deployment Protection or another login protects your app.
143+
Learn more about the [`tunnelRoute` option](/platforms/javascript/guides/nextjs/configuration/build/#tunnelRoute).
144+
142145
```typescript {filename:next.config.ts}
143146
import type { NextConfig } from "next";
144147
import { withSentryConfig } from "@sentry/nextjs/config";

‎docs/platforms/javascript/guides/nextjs/manual-setup/index.mdx‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@ Prevent ad blockers from blocking Sentry events by routing them through your Nex
254254
This increases server load. Consider the trade-off for your application.
255255
</Alert>
256256

257+
To forward only the request body to Sentry, also add `app/sentry-tunnel/route.ts` (SDK version `10.77.1` and later on v10, `11.9.0` and later on v11).
258+
Use this file if Vercel Deployment Protection or another login protects your app. Learn more <PlatformLink to="/configuration/build/#tunnelRoute">in `tunnelRoute`</PlatformLink>.
259+
257260
<Expandable title="Using Next.js proxy (middleware) with tunneling">
258261

259262
If you're using Next.js proxy/middleware that intercepts requests, exclude the tunnel route. The file is called `proxy.ts` in Next.js 16+ and `middleware.ts` in Next.js 15:
@@ -273,6 +276,11 @@ export default withSentryConfig(nextConfig, {
273276
});
274277
```
275278

279+
```typescript {filename:app/sentry-tunnel/route.ts}
280+
// Forward only the request body to Sentry
281+
export { POST } from "@sentry/nextjs/tunnel";
282+
```
283+
276284
### Control the Data You Send to Sentry (Optional)
277285

278286
<PlatformContent includePath="getting-started-data-collection" />

‎docs/platforms/javascript/guides/nextjs/manual-setup/webpack-setup.mdx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ Tunneling works identically for both Webpack and Turbopack. Sentry automatically
222222

223223
Enable tunneling to bypass ad-blockers. Use a fixed route so you can exclude it from middleware if needed.
224224

225+
To forward only the request body to Sentry, add a tunnel route handler (SDK version `10.77.1` and later on v10, `11.9.0` and later on v11).
226+
Use the handler if Vercel Deployment Protection or another login protects your app. Learn more about the [`tunnelRoute` option](/platforms/javascript/guides/nextjs/configuration/build/#tunnelRoute).
227+
225228
<Expandable title="Using random tunnel routes">
226229

227230
You can use `tunnelRoute: true` to auto-generate a random route. However, random routes cannot be excluded from middleware matchers since the route changes on each build. This may cause issues if you have middleware that intercepts all requests.

‎docs/platforms/javascript/guides/tanstackstart-react/features/createSentryTunnelRoute.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ description: "Learn how to manually define a Sentry tunnel route in your TanStac
1313
the route file, the DSN allowlist, or can't use the Vite plugin.
1414
</Alert>
1515

16-
The `createSentryTunnelRoute` helper returns a TanStack Start server-route configuration with a `POST` handler that forwards Sentry envelopes to Sentry's ingest servers. Use it inside a file route to expose a same-origin tunnel endpoint.
16+
The `createSentryTunnelRoute` helper returns a TanStack Start server-route configuration with a `POST` handler that forwards Sentry envelopes to Sentry's ingest servers. Use it inside a file route to expose a same-origin tunnel endpoint. It forwards only the request body to Sentry.
1717

1818
## Usage
1919

‎docs/platforms/javascript/guides/tanstackstart-react/features/sentryTanstackStart.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ sentryTanstackStart({
9797

9898
<AvailableSince version="10.51.0" />
9999

100-
The `tunnelRoute` option registers a same-origin TanStack Start server route that forwards Sentry envelopes to Sentry's ingest servers. It also automatically sets the client `tunnel` option in `Sentry.init()` so browser events go through that route. This helps avoid ad blockers and corporate firewalls that block requests to `*.sentry.io`.
100+
The `tunnelRoute` option registers a same-origin TanStack Start server route that forwards Sentry envelopes to Sentry's ingest servers. It also automatically sets the client `tunnel` option in `Sentry.init()` so browser events go through that route. This helps avoid ad blockers and corporate firewalls that block requests to `*.sentry.io`. It forwards only the request body to Sentry.
101101

102102
`tunnelRoute` accepts three shapes:
103103

‎platform-includes/getting-started-tunneling/javascript.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
You can prevent ad blockers from blocking Sentry events using tunneling. Use the `tunnel` option in `Sentry.init` to add an API endpoint in your application that forwards Sentry events to Sentry servers.
22

3-
This will send all events to the `tunnel` endpoint. However, the events need to be parsed and redirected to Sentry, so you'll need to do additional configuration on the server. You can find a detailed explanation on how to do this on our <PlatformLink to="/troubleshooting/#using-the-tunnel-option"> Troubleshooting page</PlatformLink>.
3+
This will send all events to the `tunnel` endpoint. However, the events need to be parsed and redirected to Sentry, so you'll need to do additional configuration on the server. The endpoint must forward only the request body to Sentry and accept only your own DSN. You can find a detailed explanation on how to do this on our <PlatformLink to="/troubleshooting/#using-the-tunnel-option"> Troubleshooting page</PlatformLink>.
44

55
```javascript {3}
66
Sentry.init({

0 commit comments

Comments
 (0)