If you’re familiar with New Relic browser monitoring, you might be using single-page application (SPA) monitoring for deeper visibility and actionable insights into real user interactions with single-page apps. After you set up your single-page app with an SPA-enabled version of the browser monitoring agent, you’re excited to capture all the metrics and traces from your app. But, wait! Distributed tracing isn’t working!

The agent shows that distributed tracing is enabled by default, but you can’t capture distributed trace details. Several thoughts go through your mind… Is it because the backend service doesn’t have an agent? Or is it because the backend service is deployed separately from your web service? Maybe it’s both. Help!

This blog post will help you with SPA monitoring, so you can take advantage of distributed tracing to find out what your users are experiencing, fix issues quickly, and make customers happy.

Example scenario

Architecture diagram for an example scenario for a single-page application.

In this scenario, let’s say you have a full-stack JavaScript application. Your frontend (you might be using React, Angular, or Vue, for example) is hosted on a static web host (which might be something like Netlify, Amazon S3, Render, or GitHub Pages). Your backend web service could be hosted on an Amazon Elastic Compute Cloud, an Amazon Elastic Block Store, or on Google Cloud Platform with Google Engine Compute Engine or Google App Engine.

The name of your frontend browser app is www.spa-app.awesome.com.

The name of your backend web service is www.robust-webservice.awesome.com.

Configure New Relic for SPA and Node.js APM agents

You're setting up New Relic for two application performance monitoring (APM) agents: SPA and Node.js. That means you configure your applications with these New Relic agents:

The problem

As you can see in this screen capture, when you configure your settings at Browser > Settings > Applications settings, you see the default browser agent configuration Pro+SPA and can set Distributed Tracing to ON

But with the application hosting model described earlier, this setting does not show any traces.

Three attempted fixes

I’ll walk through three ways you might try to fix the problem. Let’s assume you are using the application model described in the example scenario section.

Spoiler alert: As I’ll show with our example scenario in this blog post, the third attempt will work.

Attempted fix #1: Enable CORS

If your frontend app and backend service are hosted on different subdomains or hosts, you might want to enable cross-origin resource sharing, also called CORS, for the browser agent. Select Browser > Settings > Applications settings again. Include the base URL for your backend service or API, as shown in this screenshot.

But the result for enabling CORS still doesn’t help. You don’t see any distributed traces!

Attempted fix #2: Enable CORS with New Relic headers

OK, let’s try again. Go back to Browser > Settings > Applications settings again. 

This time, under Cross-Origin Resource Sharing (CORS), add origins. Select Use newrelic header and Use trace context headers, as shown in the next screen capture.

You hope that if you add origins, you’ll see the distributed traces for your cross-origin AJAX calls. 

The result? Still no traces!

Attempted fix #3: Accept the CORS headers

Look at the previous screenshot again. When you’re in the Browser > Settings > Application settings, you’ll notice that the Cross-Origin Resource Sharing (CORS) section includes a warning about potential failure of AJAX requests if the New Relic custom header or WC3 trace context headers are not accepted.

If there aren’t any failures in AJAX requests from the frontend app, you might overlook or ignore this message.

But this is the answer to your problem!

You need to both accept and return these headers as part of the response to the AJAX/API calls from the frontend application.

Use this example to allow the headers:

/* Node+Express JS Sample */

//set custom headers
app.use(function(req, res, next) {
  res.setHeader(
    "Access-Control-Allow-Headers", ["newrelic","traceparent","tracestate"]
   );
  return next();
});

Here are your results: Finally, you see your traces! In this example, you can see 2 entities: angular12-spa and angular12-nodeService. This is the distributed trace from your browser app to your backend service.