Frontend-to-backend trace propagation ΒΆ

By default, Faro collects browser-side traces but they're not connected to your backend spans in Tempo. With trace propagation, you get end-to-end visibility: a single trace that follows a user action from the browser through your backend services.

Service topology showing dp-saksbehandling-frontend connected to backend services via traces

A few applications on Nais use this today (dp-saksbehandling-frontend, dp-brukerdialog-frontend, dp-mine-dagpenger-frontend). It's quick to set up and makes debugging cross-service issues far easier.

How it works ΒΆ

  1. The browser sends a traceparent HTTP header with each API request to your backend
  2. Your backend picks up the trace context and creates child spans under the same trace
  3. Both browser and backend spans appear together in Grafana Tempo
mermaid
sequenceDiagram
    participant Browser
    participant Backend
    participant Tempo

    Browser->>Backend: GET /api/data (traceparent: 00-abc123...)
    Backend->>Backend: Process request (child span)
    Backend->>Tempo: Export backend spans
    Browser->>Tempo: Export browser spans
    Note over Tempo: Same trace ID β†’ linked

What you need ΒΆ

  1. @grafana/faro-web-tracing installed in your frontend
  2. propagateTraceHeaderCorsUrls configured in TracingInstrumentation (shown below)
  3. Backend CORS allowing the traceparent header
  4. Backend instrumented with OpenTelemetry (or auto-instrumentation) and exporting traces to Tempo

Configure trace propagation in Faro ΒΆ

Add propagateTraceHeaderCorsUrls to your TracingInstrumentation config. This tells Faro which URLs should receive the traceparent header:

typescript

Use a regex or string that matches your backend API URLs. You can list multiple patterns:

typescript

If you build URLs from environment variables, escape them to prevent ReDoS:

typescript

Configure CORS on your backend ΒΆ

Your backend must allow the traceparent header in CORS responses. Without this, the browser blocks the header and trace propagation silently fails.

Add traceparent to Access-Control-Allow-Headers:

Plaintext

If you also use tracestate (for vendor-specific trace context), allow that too:

Plaintext

Optional: Backend server-timing header ΒΆ

To correlate backend responses back to the frontend trace, your backend can send a server-timing header containing the trace context. This lets Grafana link the response back to the backend span.

In a Next.js middleware:

typescript

Verify it works ΒΆ

  1. Open your app in a browser
  2. Open DevTools β†’ Network tab
  3. Make a request to your backend API
  4. Check the Request Headers β€” you should see traceparent: 00-<traceId>-<spanId>-01
  5. If you set up the server-timing header, check the Response Headers for it
  6. In Grafana Tempo, search for the trace ID β€” you should see both browser and backend spans