diff --git a/docs/platforms/javascript/common/configuration/options.mdx b/docs/platforms/javascript/common/configuration/options.mdx index 37153b6370c62..0953eb5576478 100644 --- a/docs/platforms/javascript/common/configuration/options.mdx +++ b/docs/platforms/javascript/common/configuration/options.mdx @@ -87,6 +87,8 @@ Environments are case-sensitive. The environment name can't contain newlines, sp 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). +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). + diff --git a/docs/platforms/javascript/common/troubleshooting/index.mdx b/docs/platforms/javascript/common/troubleshooting/index.mdx index 838e9fb64f534..8d7c124582ade 100644 --- a/docs/platforms/javascript/common/troubleshooting/index.mdx +++ b/docs/platforms/javascript/common/troubleshooting/index.mdx @@ -250,7 +250,28 @@ Sentry.init({ }); ``` -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: +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. + +Your tunnel endpoint must: + +- Forward only the request body to Sentry, without the request headers. +- Accept only envelopes whose DSN is on a list of allowed DSNs. Otherwise, anyone can use your endpoint to send requests to other servers. +- Send the envelope only to the host of that DSN. + +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.78.0` (v10) and `11.9.0` (v11), it also rejects requests larger than 20 MiB. + +```javascript +import { handleTunnelRequest } from "@sentry/core"; + +export async function POST(request) { + return handleTunnelRequest({ + request, + allowedDsns: ["___PUBLIC_DSN___"], + }); +} +``` + +If you implement the endpoint yourself, here's an example for your server component: ```csharp @@ -334,7 +355,7 @@ const SENTRY_PROJECT_IDS = ["123456"]; export const postAction = async ({ request }) => { try { - const envelopeBytes = await req.arrayBuffer(); + const envelopeBytes = await request.arrayBuffer(); const envelope = new TextDecoder().decode(envelopeBytes); const piece = envelope.split("\n")[0]; const header = JSON.parse(piece); @@ -420,6 +441,32 @@ If you would like to copy and paste the snippet directly, here it is minified: + + +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. + + + +The Next.js `tunnelRoute` option handles this for you. See `tunnelRoute`. + + + +Forward only the request body, or tell the browser to send no credentials: + +```javascript +Sentry.init({ + dsn: "___PUBLIC_DSN___", + tunnel: "/tunnel", + transportOptions: { + fetchOptions: { credentials: "omit" }, + }, +}); +``` + +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. + + + diff --git a/docs/platforms/javascript/guides/cordova/configuration/options.mdx b/docs/platforms/javascript/guides/cordova/configuration/options.mdx index 6955b0c1f4e72..6906a9235b502 100644 --- a/docs/platforms/javascript/guides/cordova/configuration/options.mdx +++ b/docs/platforms/javascript/guides/cordova/configuration/options.mdx @@ -51,6 +51,8 @@ Environments are case-sensitive. The environment name can't contain newlines, sp 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). +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). + diff --git a/docs/platforms/javascript/guides/nextjs/configuration/build/index.mdx b/docs/platforms/javascript/guides/nextjs/configuration/build/index.mdx index 894b524e379ba..d4885f9e6e165 100644 --- a/docs/platforms/javascript/guides/nextjs/configuration/build/index.mdx +++ b/docs/platforms/javascript/guides/nextjs/configuration/build/index.mdx @@ -205,7 +205,7 @@ Note: Enabling this option can lead to longer build times. -This feature requires **Next.js version 11+** and doesn't currently work with self-hosted Sentry instances. +This feature requires **Next.js version 11+**. Rewrite mode doesn't work with self-hosted Sentry instances. 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: - `true` for auto-generated routes, which are unpredictable and change with each deployment. - A custom static string path like `/error-monitoring`. + + +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: + +```typescript {filename:proxy.ts} +export const config = { + matcher: ["/((?!error-monitoring|_next/static|_next/image|favicon.ico).*)"], +}; +``` + + + Learn more about tunneling in the troubleshooting section. - - If you're using Turbopack, client-side event recording will fail if your - Next.js middleware intercepts the configured tunnel route. To fix this, set - the route to a fixed string (like `/error-monitoring`) and add a negative - matcher like `(?!error-monitoring)` in your middleware to exclude the tunnel - route. If you're not using Turbopack, Sentry will automatically skip the - tunnel route in your middleware. +The tunnel route works in one of two modes: + +- **Rewrite mode** (default): A Next.js rewrite proxies each request to Sentry with all its request headers. Works only with Sentry SaaS DSNs. +- **Handler mode**: A route handler in your app forwards only the request body to Sentry. Works with any DSN. + +Starting with versions `10.78.0` (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). +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. +You can also set `credentials: "same-origin"`, but rewrite mode then forwards the credentials to Sentry: + +```javascript {filename:instrumentation-client.ts} +Sentry.init({ + dsn: "___PUBLIC_DSN___", + transportOptions: { + fetchOptions: { credentials: "same-origin" }, + }, +}); +``` + +**Handler mode** + + + +Keep `tunnelRoute` and add this file. If your app uses a `src` directory, add it at `src/app/sentry-tunnel/route.ts`: + +```typescript {filename:app/sentry-tunnel/route.ts} +export { POST } from "@sentry/nextjs/tunnel"; +``` + +This works with any `tunnelRoute` value, including `true`. When the SDK finds the file, the build prints this line: + +```bash +[@sentry/nextjs] The tunnel route sends events through app/sentry-tunnel/route.ts. Only the event payload goes to Sentry. +``` + +If the line is missing, the SDK uses rewrite mode. + +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: + +```typescript {filename:app/sentry-tunnel/route.ts} +import { createSentryTunnelRoute } from "@sentry/nextjs/tunnel"; + +export const { POST } = createSentryTunnelRoute({ + allowedDsns: ["___PUBLIC_DSN___"], +}); +``` + + + +- Handler mode works only with the App Router. +- The SDK doesn't find the file in a route group, such as `app/(group)/sentry-tunnel/`. +- With `"moduleResolution": "node"` in `tsconfig.json`, the type check fails with `Cannot find module '@sentry/nextjs/tunnel'`. Set `moduleResolution` to `bundler`, `node16`, or `nodenext`. +- Each event request runs the route handler. On serverless hosts, this is one function invocation for each request. + diff --git a/docs/platforms/javascript/guides/nextjs/index.mdx b/docs/platforms/javascript/guides/nextjs/index.mdx index 761c52c7e23e1..7eaa995d70403 100644 --- a/docs/platforms/javascript/guides/nextjs/index.mdx +++ b/docs/platforms/javascript/guides/nextjs/index.mdx @@ -144,6 +144,9 @@ export const onRequestError = Sentry.captureRequestError; Your `next.config.ts` is wrapped with `withSentryConfig` to enable source map uploads, tunneling (to avoid ad-blockers), and other build-time features. +To forward only the request body to Sentry, add a tunnel route handler (SDK version `10.78.0` and later on v10, `11.9.0` and later on v11). 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). + diff --git a/docs/platforms/javascript/guides/nextjs/manual-setup/index.mdx b/docs/platforms/javascript/guides/nextjs/manual-setup/index.mdx index 86c9379b31a28..096ec0996d65b 100644 --- a/docs/platforms/javascript/guides/nextjs/manual-setup/index.mdx +++ b/docs/platforms/javascript/guides/nextjs/manual-setup/index.mdx @@ -326,6 +326,9 @@ Prevent ad blockers from blocking Sentry events by routing them through your Nex This increases server load. Consider the trade-off for your application. +To forward only the request body to Sentry, also add `app/sentry-tunnel/route.ts` (SDK version `10.78.0` and later on v10, `11.9.0` and later on v11). +Use this file if Vercel Deployment Protection or another login protects your app. Learn more in `tunnelRoute`. + 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: @@ -348,6 +351,11 @@ export default withSentryConfig(nextConfig, { }); ``` +```typescript {filename:app/sentry-tunnel/route.ts} +// Forward only the request body to Sentry +export { POST } from "@sentry/nextjs/tunnel"; +``` + diff --git a/docs/platforms/javascript/guides/nextjs/manual-setup/webpack-setup.mdx b/docs/platforms/javascript/guides/nextjs/manual-setup/webpack-setup.mdx index c375210378619..5ddec5cf77175 100644 --- a/docs/platforms/javascript/guides/nextjs/manual-setup/webpack-setup.mdx +++ b/docs/platforms/javascript/guides/nextjs/manual-setup/webpack-setup.mdx @@ -331,6 +331,9 @@ Tunneling works identically for both Webpack and Turbopack. Sentry automatically Enable tunneling to bypass ad-blockers. Use a fixed route so you can exclude it from middleware if needed. +To forward only the request body to Sentry, add a tunnel route handler (SDK version `10.78.0` and later on v10, `11.9.0` and later on v11). +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). + 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. diff --git a/docs/platforms/javascript/guides/react/index.mdx b/docs/platforms/javascript/guides/react/index.mdx index cb8c7a67e2653..bc6b898abe556 100644 --- a/docs/platforms/javascript/guides/react/index.mdx +++ b/docs/platforms/javascript/guides/react/index.mdx @@ -297,7 +297,7 @@ const store = createStore(rootReducer, compose(sentryEnhancer)); ### Avoid Ad Blockers With Tunneling (Optional) -Some ad blockers prevent Sentry events from being sent. Use the `tunnel` option to route events through your server. +Some ad blockers prevent Sentry events from being sent. Use the `tunnel` option to route events through your server. Your server must forward only the request body to Sentry and accept only your own DSN. Learn more about [setting up tunneling](/platforms/javascript/guides/react/troubleshooting/#using-the-tunnel-option). diff --git a/docs/platforms/javascript/guides/tanstackstart-react/features/createSentryTunnelRoute.mdx b/docs/platforms/javascript/guides/tanstackstart-react/features/createSentryTunnelRoute.mdx index 0242e493afbca..d3244d9717c84 100644 --- a/docs/platforms/javascript/guides/tanstackstart-react/features/createSentryTunnelRoute.mdx +++ b/docs/platforms/javascript/guides/tanstackstart-react/features/createSentryTunnelRoute.mdx @@ -13,7 +13,7 @@ description: "Learn how to manually define a Sentry tunnel route in your TanStac the route file, the DSN allowlist, or can't use the Vite plugin. -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. +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. ## Usage diff --git a/docs/platforms/javascript/guides/tanstackstart-react/features/sentryTanstackStart.mdx b/docs/platforms/javascript/guides/tanstackstart-react/features/sentryTanstackStart.mdx index ab4ae0a30a655..202086bd256c2 100644 --- a/docs/platforms/javascript/guides/tanstackstart-react/features/sentryTanstackStart.mdx +++ b/docs/platforms/javascript/guides/tanstackstart-react/features/sentryTanstackStart.mdx @@ -97,7 +97,7 @@ sentryTanstackStart({ -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`. +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. `tunnelRoute` accepts three shapes: diff --git a/platform-includes/getting-started-tunneling/javascript.mdx b/platform-includes/getting-started-tunneling/javascript.mdx index 39962565e41c6..4c4ac4eef4bff 100644 --- a/platform-includes/getting-started-tunneling/javascript.mdx +++ b/platform-includes/getting-started-tunneling/javascript.mdx @@ -4,7 +4,7 @@ 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. -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 Troubleshooting page. +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 Troubleshooting page. diff --git a/platform-includes/getting-started-tunneling/javascript.sveltekit.mdx b/platform-includes/getting-started-tunneling/javascript.sveltekit.mdx index c7034fe06cac0..e7227fcc3afbd 100644 --- a/platform-includes/getting-started-tunneling/javascript.sveltekit.mdx +++ b/platform-includes/getting-started-tunneling/javascript.sveltekit.mdx @@ -4,7 +4,7 @@ You can prevent ad blockers from blocking Sentry events using tunneling. Use the `tunnel` option inside your `hooks.client.(js|ts)` file's `Sentry.init` to add an API endpoint in your application that forwards Sentry events to Sentry servers. -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 [Troubleshooting page](/platforms/javascript/guides/sveltekit/troubleshooting/#using-the-tunnel-option). +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 [Troubleshooting page](/platforms/javascript/guides/sveltekit/troubleshooting/#using-the-tunnel-option).