Correlate RUM traces with OpenTelemetry backends
Connect a request from your web app to the backend trace behind it, so you can follow a slow call in a session through to the service that made it slow.
The SDK already adds W3C context propagation headers to fetch and XHR requests made to the same origin, so those are stitched together with no setup.
To reach a backend on a different origin, configure the SDK to send the headers, then allow those headers in your backend’s CORS policy. If you do the first without the second, the browser blocks every call to that origin.
Send the trace headers from the browser
Section titled “Send the trace headers from the browser”Name the cross-origin targets in propagateTraceHeaderCorsUrls. Each entry is a substring or a regular expression, and it covers both fetch and XMLHttpRequest. To propagate trace context to https://api.example.com:
This adds context propagation headers to those requests. The backend can then create its spans from that context.
Allow the trace headers in your backend’s CORS policy
Section titled “Allow the trace headers in your backend’s CORS policy”traceparent isn’t a CORS-safelisted request header, so adding it turns every cross-origin call into a preflighted one. Your backend must name it in the Access-Control-Allow-Headers response header. If it doesn’t, the browser blocks the request before it’s sent:
Add traceparent to the allowed headers, along with tracestate and baggage if your setup propagates them. With Express and the cors middleware:
Other stacks set the same header under a different name: CORS_ALLOW_HEADERS in django-cors-headers, allowed_headers in rack-cors, Access-Control-Allow-Headers in an nginx add_header directive.
Troubleshoot a broken correlation
Section titled “Troubleshoot a broken correlation”Match what you’re seeing in the browser to the fix:
| Symptom | Cause | Fix |
|---|---|---|
| Requests succeed, but the browser session and the backend show separate traces | The SDK never attached traceparent, so the backend started its own trace | Add the origin to propagateTraceHeaderCorsUrls |
Requests fail with Request header field traceparent is not allowed by Access-Control-Allow-Headers | The SDK attached traceparent, but the backend’s preflight response rejects it | Add traceparent to Access-Control-Allow-Headers |
To confirm the header is arriving, log req.headers.traceparent on the backend. If it has a value, the trace is stitched, and the ID it carries is the same one on the browser span.
After that, View full backend trace on a request row in What happened opens the distributed trace behind that call.
Example
Section titled “Example”With RUM and the backend both instrumented, a request row in a session opens the backend trace behind it:

The same trace is available from APM:
