Snippet

Browser W3C tracing with Application Insights

A complete browser module that starts a W3C trace and attaches traceparent only to your own API.

javascriptOct 6, 2026MIT

AI-generated, human-reviewed: the first draft was generated by an AI agent; I fact-checked, tested, and edited it. AI use policy

What it does#

Starts a W3C trace in the browser and adds traceparent to requests sent to your API’s host, so the scrubbed browser dependency span and the server span land in the same trace. It does not auto-record page views, browser routes, full URLs, query strings, request headers, response headers, exceptions, bodies, or cookies. The initializer keeps only scrubbed API dependency telemetry when the target host equals your API host, with a fixed route label such as POST /api; every other telemetry envelope is dropped.

You need#

  • @microsoft/applicationinsights-web 3.4.4 (npm install @microsoft/applicationinsights-web@3.4.4)
  • A Vite app (the module reads import.meta.env)
  • Two environment variables in .env:
    • VITE_APPLICATIONINSIGHTS_CONNECTION_STRING: your Application Insights connection string
    • VITE_API_BASE_URL: the URL the browser calls, for example your API gateway

Where it goes#

Save it as src/telemetry.js and import it once, before your first API call: import "./telemetry.js";

src/telemetry.js
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Sarang Datye
import { ApplicationInsights, DistributedTracingModes } from "@microsoft/applicationinsights-web";

const connectionString = import.meta.env.VITE_APPLICATIONINSIGHTS_CONNECTION_STRING;
const apiBaseUrl = import.meta.env.VITE_API_BASE_URL;
const apiHost = apiBaseUrl ? new URL(apiBaseUrl).host : undefined;

if (!connectionString) {
  // Fail loudly in the console rather than silently dropping telemetry.
  // eslint-disable-next-line no-console
  console.warn(
    "VITE_APPLICATIONINSIGHTS_CONNECTION_STRING is not set; browser telemetry will not be sent."
  );
}

/**
 * Use the same Application Insights resource as your API so both sides share one trace.
 * W3C-only distributed tracing: no legacy Request-Id/Request-Context headers.
 * Sensitive data (prompts, responses, headers) is never captured as telemetry.
 */
export const appInsights = new ApplicationInsights({
  config: {
    connectionString,
    distributedTracingMode: DistributedTracingModes.W3C,
    enableCorsCorrelation: true,
    correlationHeaderDomains: apiHost ? [apiHost] : [],
    enableAutoRouteTracking: false,
    disablePageViewTracking: true,
    disableExceptionTracking: true,
    autoTrackPageVisitTime: false,
    enableUnhandledPromiseRejectionTracking: false,
    enableRequestHeaderTracking: false,
    enableResponseHeaderTracking: false,
    enableAjaxErrorStatusText: false,
    enableAjaxPerfTracking: false,
    disableCookiesUsage: true,
    samplingPercentage: 100,
  },
});

appInsights.loadAppInsights();

appInsights.addTelemetryInitializer((envelope) => {
  if (envelope.baseType !== "RemoteDependencyData") {
    return false;
  }

  const dependency = envelope.baseData;
  const dependencyHost = hostFromUrl(dependency?.data) ?? dependency?.target;
  if (!apiHost || dependencyHost !== apiHost) {
    return false;
  }

  const method = dependency?.name?.match(/^(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\b/u)?.[1];
  if (!method) {
    return false;
  }

  dependency.name = `${method} /api`;
  dependency.target = apiHost;
  delete dependency.data;
  delete dependency.properties;
  delete dependency.measurements;
  delete envelope.data;
  return true;
});

function hostFromUrl(value) {
  try {
    return value ? new URL(value).host : undefined;
  } catch {
    return undefined;
  }
}

Check that it works#

Open the browser’s developer tools, send a request to your API, and look at the request headers: traceparent is there. Requests to any other host do not get it.