Skip to content
This repository was archived by the owner on Oct 13, 2026. It is now read-only.
This repository was archived by the owner on Oct 13, 2026. It is now read-only.

Replay data isn't available to tunnel middleware if Sentry tunnel is enabled #562

Closed

Description

@ryan-cahill

Version

@nuxtjs/sentry: 7.2.1
nuxt: 2.16.1

Sentry configuration

publicRuntimeConfig: {
  ...
      sentry: {
        config: {
          dsn: '<removed>',
          ...
        },
        clientConfig: {
          tunnel: '/tunnel/sentry',
        },
      },
    },
...
    sentry: {
      dsn: '<removed>',
      clientConfig: {
        replaysSessionSampleRate: process.env.ENVIRONMENT === 'local' ? 1.0 : 0.1,
        replaysOnErrorSampleRate: 1.0,
      },
      clientIntegrations: {
        Replay: {},
      },
    },
    ....
    serverMiddleware: [
      { path: '/tunnel/sentry', handler: 'server-middleware/sentry.tunnel.ts' },
    ]
    ...
  ...

Sentry server middleware

// src/server-middleware/sentry.tunnel.ts

import axios from 'axios';
import express from 'express';

// https://github.com/getsentry/examples/blob/a9f50106988ce2d21e41ab8584ebaccef4988709/tunneling/nextjs/pages/api/tunnel.js

// Change host appropriately if you run your own Sentry instance.
const sentryHost = '.sentry.io';

// Set knownProjectIds to an array with your Sentry project IDs which you
// want to accept through this proxy.
const knownProjectIds = ['<removed>'];

const middleware = express();

middleware.use(express.text({ limit: '50mb' }));

middleware.all('/', async (req, res) => {
  try {
    const envelope = req.body;

    const pieces = envelope.split('\n');

    const header = JSON.parse(pieces[0]);

    const { host, pathname } = new URL(header.dsn);
    if (!host.endsWith(sentryHost)) {
      throw new Error(`invalid host: ${host}`);
    }

    const projectId = pathname.slice(1);
    if (!knownProjectIds.includes(projectId)) {
      throw new Error(`invalid project id: ${projectId}`);
    }

    const url = `https://${host}/api/${projectId}/envelope/`;
    const response = await axios.post(url, envelope, { timeout: 10000 });
    res.end(JSON.stringify(response.data));
  } catch (e) {
    console.error(e);
    res.statusCode = 400;
    res.end(JSON.stringify({ status: 'invalid request' }));
  }
});

export default middleware;

Steps to reproduce

  • Add server middleware to handle Sentry requests such src/server-middleware/sentry.tunnel.ts above
  • Update Sentry configuration to the settings above
  • Start application
  • Throw a new error anywhere in an application mounted() function in order to trigger a Sentry error
  • Notice that in the middleware, req.body is just an empty object

What is Expected?

There should be some kind of data representing the replay event in req.body in the tunnel middleware to forward to Sentry

What is actually happening?

req.body is just an empty object in the tunnel middleware, so there's no replay data to forward to Sentry

Activity

  1. rchl commented on Apr 19, 2023

    @rchl
    Contributor

    It hardly looks like a problem with this module.

    I would guess that Sentry payload doesn't have text/* type so express.text probably is not parsing it. You should probably inspect the request that Sentry makes on the client side and adjust the parsing middleware so that it handles that type of payload.

  2. rchl commented on Apr 19, 2023

    @rchl
    Contributor

    Based on some fix I did in sentry-testkit, maybe a middleware like bodyParser.text({ type: () => true }) would be more effective. See https://github.com/zivl/sentry-testkit/blob/d5d0685e561f7bd3ed387c745365ef64593eb36b/src/localServerApi.ts#L23-L25

  3. ryan-cahill commented on Apr 20, 2023

    @ryan-cahill
    ContributorAuthor

    Thanks for the suggestion @rchl. That did seem to get the issue closer to being resolved, but there was still a missing piece that I didn't figure out. Seeing as I'm just evaluating the Session Replay feature, I didn't spend too much time continuing down that road. I found that the setting below solved the issue for now and Session Replay worked using the tunnel as expected. I found this from a similar issue in another repo

    // nuxt.config.ts
    ...
          clientIntegrations: {
            Replay: {
              useCompression: false,
    ...
  4. rchl commented on Apr 20, 2023

    @rchl
    Contributor

    As explained by @lforst in the linked issue:

    Hi, this probably doesn't work because replay uses a binary format for it's payloads and nextjs automatically bodyparses.

    express.text is essentially body-parser so same issue applies here.

    I would suggest trying @nuxtjs/proxy module instead of a custom server middleware.

    Something like this might work:

      modules: [
        '@nuxtjs/proxy',
        '@nuxtjs/sentry',
      ],
      proxy: {
        '/tunnel/sentry': {
          target: 'https://[xxx].ingest.sentry.io/api/[projectId]/envelope/',
          ignorePath: true,
        },
      },
      // ...
    }

    Of course the [projectId] and [xxx] needs to be replaced with actual data from the DSN.

    (This setup is not as dynamic as the one suggested by Sentry where Sentry origin and project ID is parsed automatically from the request URL but it might be good enough.)

  5. locked and limited conversation to collaborators on May 10, 2023
  6. converted this issue into a discussion #569 on May 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions