DebugBundle
SDKs

Browser Relay Setup

Route browser SDK events through your backend for credential isolation and origin validation.

The browser relay lets your Browser SDK send events through your own backend instead of directly to the DebugBundle ingestion API. This keeps your project token on the server and adds origin validation.

Why Use a Relay?

ConcernDirect TransportRelay Transport
Project token exposureToken in client-side codeToken stays on server
Origin validationNoneServer validates request origin
Rate limitingPer-client IPCentralized per-IP limiting
Durable writesBrowser-only (beacon)Server-side spool to disk
Works without backend❌ Requires your backend

If your app has no backend (static site, SPA hosted on CDN), use direct transport instead. The Browser SDK uses beacon/keepalive for reliable delivery on page unload.

Architecture

┌──────────────────────────────────────────────────┐
│  Browser                                         │
│  ┌─────────────────┐                             │
│  │  Browser SDK     │──── POST /debugbundle/browser ──┐
│  │  (relay mode)    │                             │   │
│  └─────────────────┘                             │   │
└──────────────────────────────────────────────────┘   │

┌──────────────────────────────────────────────────┐
│  Your Backend                                    │
│  ┌─────────────────┐                             │
│  │  Relay Handler   │──  validates origin        │
│  │                  │──  rate limits per-IP      │
│  │                  │──  attaches project token  │
│  └────────┬────────┘                             │
│           │                                      │
└───────────┼──────────────────────────────────────┘

            ▼  POST /v1/events (with project token)
┌──────────────────────────────────────────────────┐
│  DebugBundle Ingestion API                       │
└──────────────────────────────────────────────────┘

Relay Configuration

The relay handler accepts these options:

OptionTypeDefaultDescription
projectTokenstringProject token for ingestion auth.
allowedOriginsstring[]Auto from HostOrigins permitted to send events. If empty, validates against Host/X-Forwarded-Host.
rateLimitPerMinutenumber60Max requests per IP per minute.
maxBodyBytesnumber262144 (256 KB)Max request body size. Requests exceeding this are rejected.
durableWritebooleanfalsePersist events to disk spool before uploading. Survives server restarts.
spoolDirstringDirectory for durable write spool files.
endpointstringhttps://api.debugbundle.com/v1/eventsOverride ingestion endpoint (for self-hosted).
environmentstringOverride service.environment on relayed events.
servicestringOverride service.name on relayed events.

Accepted Event Types

The relay only accepts browser-origin event types:

  • frontend_exception
  • error_suppressed
  • frontend_breadcrumb
  • probe_event

Server-side event types (backend_exception, request_event, log_event) are rejected with 400 Bad Request.

Express

const express = require('express');
const { debugBundleRelay } = require('@debugbundle/sdk-node');

const app = express();

app.post('/debugbundle/browser', debugBundleRelay({
  projectToken: process.env.DEBUGBUNDLE_PROJECT_TOKEN,
  allowedOrigins: ['https://myapp.com', 'https://staging.myapp.com'],
}));

app.listen(3000);

The Express relay handler parses the request body, validates the origin, applies rate limiting, and forwards accepted events to the ingestion API.

Fastify

const fastify = require('fastify')();
const { debugBundleRelayPlugin } = require('@debugbundle/sdk-node');

await fastify.register(debugBundleRelayPlugin, {
  projectToken: process.env.DEBUGBUNDLE_PROJECT_TOKEN,
  routePath: '/debugbundle/browser',
  allowedOrigins: ['https://myapp.com'],
});

await fastify.listen({ port: 3000 });

The Fastify plugin registers as a standard route plugin. Pass routePath to customize the endpoint path (default: /debugbundle/browser).

Next.js (App Router)

// app/api/debugbundle/browser/route.ts
import { createNextjsRelayHandler } from '@debugbundle/sdk-node';

const handler = createNextjsRelayHandler({
  projectToken: process.env.DEBUGBUNDLE_PROJECT_TOKEN,
  allowedOrigins: ['https://myapp.com'],
});

export async function POST(request: Request): Promise<Response> {
  return handler(request);
}

The Next.js handler uses the Web Request/Response APIs. It extracts the client IP from X-Forwarded-For or X-Real-IP headers.

Browser SDK Configuration

Point the Browser SDK at your relay endpoint:

import debugbundle from '@debugbundle/sdk-browser';

debugbundle.init({
  // No projectToken needed on the client — the relay adds it
  endpoint: '/debugbundle/browser',
  transport: 'relay',
});

When using relay transport, do not set projectToken in the Browser SDK config. The relay handler adds the token server-side.

Origin Validation

The relay validates the request origin to prevent unauthorized event submission:

  1. With allowedOrigins: The relay extracts the origin from the Origin header (or falls back to Referer). The origin is normalized and compared against the allowlist.

  2. Without allowedOrigins: The relay checks that the request origin matches the Host or X-Forwarded-Host header (same-origin validation).

Requests that fail origin validation receive 403 Forbidden.

Rate Limiting

The relay includes per-IP rate limiting:

  • Default: 60 requests per minute per IP address
  • Tracking: In-memory sliding window
  • Exceeded: 429 Too Many Requests response
  • Header: Retry-After header included in 429 responses

Configure with rateLimitPerMinute:

debugBundleRelay({
  projectToken: process.env.DEBUGBUNDLE_PROJECT_TOKEN,
  rateLimitPerMinute: 120, // 2 requests per second
});

Durable Writes

For critical applications, enable durable writes to persist events to disk before uploading:

debugBundleRelay({
  projectToken: process.env.DEBUGBUNDLE_PROJECT_TOKEN,
  durableWrite: true,
  spoolDir: '/var/spool/debugbundle',
});

With durable writes enabled:

  1. Events are written to the spool directory immediately
  2. A background process uploads spooled events to the ingestion API
  3. If the server restarts, unuploaded events are retried on next startup

Trace Correlation

When using relay transport, the Browser SDK still injects X-DebugBundle-Trace-Id headers into all outgoing fetch() and XMLHttpRequest calls. Your backend SDK middleware reads this header and attaches the trace ID to server-side events, linking frontend breadcrumbs to backend exceptions in the same bundle.

Next Steps

On this page