<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Elastic Observability Labs - Articles by David Luna</title>
        <link>https://www.elastic.co/observability-labs</link>
        <description>Trusted security news &amp; research from the team at Elastic.</description>
        <lastBuildDate>Mon, 20 Apr 2026 16:45:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Elastic Observability Labs - Articles by David Luna</title>
            <url>https://www.elastic.co/observability-labs/assets/observability-labs-thumbnail.png</url>
            <link>https://www.elastic.co/observability-labs</link>
        </image>
        <copyright>© 2026. Elasticsearch B.V. All Rights Reserved</copyright>
        <item>
            <title><![CDATA[OpenTelemetry browser instrumentation using EDOT Browser & Kibana]]></title>
            <link>https://www.elastic.co/observability-labs/blog/edot-browser-rum</link>
            <guid isPermaLink="false">edot-browser-rum</guid>
            <pubDate>Mon, 20 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A step-by-step guide on OpenTelemetry browser instrumentation. Learn how to add EDOT Browser to a web app, export browser telemetry via OTLP, and verify traces, spans, and service maps in Kibana.]]></description>
            <content:encoded><![CDATA[<p>Instrumenting the browser is key to understanding how users truly experience your web application.
It lets you track frontend activity, such as page loads and interactions, giving visibility into performance and issues your users face in real time.
Browser instrumentation links what happens on the client to backend services, allowing you to see the full picture and troubleshoot end-to-end.
Getting started with OpenTelemetry in the browser has traditionally meant wrestling with multiple dependencies and a complex setup.
We built EDOT Browser to make this simpler, so you can instrument web apps quickly with a streamlined experience.
While EDOT Browser is in tech preview today, once generally available, it will be Elastic's supported, production-ready distribution of the OpenTelemetry Browser SDK.
With EDOT Browser, you'll have an easier onboarding path today and official support through Elastic once the project reaches general availability.</p>
<p>This guide walks through a practical way to add EDOT Browser to a web application, send browser telemetry to Elastic, and verify the results in Kibana.</p>
<h2>What you'll build</h2>
<p>In this guide, you'll add EDOT Browser to your web app and make sure it is initialized early in the application's lifecycle.
You'll configure an export path for OTLP data and set up a reverse proxy to handle authentication and CORS.</p>
<p>As you generate browser activity, the resulting telemetry will be viewable in Kibana.
You should see <code>external.http</code> spans for browser fetch and XHR requests, user interaction spans such as <code>click</code> and <code>submit</code>, and full traces that connect browser activity to backend services.
All of this data will be accessible through Kibana's Discover and Service Maps.</p>
<h2>Prerequisites</h2>
<p>Before you start, make sure you have:</p>
<ul>
<li>a web application you can modify</li>
<li>access to Elastic Observability</li>
<li>an OTLP destination:
<ul>
<li>Elastic Managed OTLP, or</li>
<li>an EDOT / OpenTelemetry Collector</li>
</ul>
</li>
<li>a reverse proxy or web server layer you can configure</li>
<li>an API key or other auth mechanism for the OTLP destination</li>
</ul>
<p><strong>Important:</strong></p>
<ul>
<li>Do not embed API keys directly in browser code.</li>
<li>Do not run EDOT Browser alongside another browser APM or RUM agent. Running multiple browser agents can cause duplicate telemetry, conflicting instrumentation, or unexpected behavior.</li>
</ul>
<h2>Architecture overview</h2>
<p>A recommended production-friendly flow looks like this:</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/edot-browser-rum/architecture.jpg" alt="Architecture overview" /></p>
<p>Using a reverse proxy offers several advantages in this architecture.
It injects the <code>Authorization</code> header on the server side, keeping credentials out of frontend code.
A reverse proxy also simplifies Cross-Origin Resource Sharing (CORS) management and handles preflight OPTIONS requests automatically.
Additionally, it provides a natural place to implement rate limiting or apply other traffic controls.</p>
<h2>Step-by-step guide</h2>
<h3>Step 1: Add EDOT Browser to your web app</h3>
<p>Install the EDOT Browser package in your frontend project. EDOT Browser is a single package that bundles multiple OpenTelemetry dependencies and provides opinionated defaults for common browser telemetry use cases.</p>
<p>Add the bundle to your app the same way you add other assets in your frontend project. You can download it from the <a href="https://github.com/elastic/elastic-otel-rum-js/releases">GitHub releases</a> page. The files you need are:</p>
<ul>
<li><code>elastic-otel-browser.min.js</code>: this is the bundle file.</li>
<li><code>elastic-otel-browser.min.js.map</code>: this is the map file in case you want to debug.</li>
</ul>
<p><strong>Note:</strong> EDOT Browser is also distributed as an NPM package, and the integration with your web app depends on the framework you use. From more details, please refer to the <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/browser">EDOT Browser reference documentation</a>.</p>
<h3>Step 2: Initialize EDOT Browser early</h3>
<p>Initialize EDOT Browser as early as possible in your application startup so it can capture the following telemetry:</p>
<ul>
<li>document and page load activity</li>
<li>user interactions</li>
<li>browser network requests</li>
<li>Core Web Vitals</li>
<li>browser-side runtime and performance metrics</li>
</ul>
<p>Assuming you placed the bundle in <code>/assets</code> path of your web app. A simplified example looks like this:</p>
<pre><code class="language-html">&lt;script src=&quot;/assets/elastic-otel-browser.min.js&quot;&gt;&lt;/script&gt;
&lt;script&gt;
  startBrowserSdk({
    serviceName: 'frontend-web-app',
    otlpEndpoint: '/otlp',
  });
&lt;/script&gt;
&lt;/html&gt;
</code></pre>
<p>The exact initialization API and option names should follow the EDOT Browser setup/configuration docs for your version, but conceptually you need:</p>
<ul>
<li>a service name</li>
<li>an OTLP HTTP endpoint</li>
<li>any optional configuration your team wants to enable</li>
</ul>
<p>Initializing EDOT Browser as early as possible in your application's startup sequence is crucial.
Doing so ensures that you capture important telemetry, such as the initial page load, early user interactions, and any network activity that occurs right after the application loads.
Delayed initialization can result in missing these valuable signals.</p>
<h3>Step 3: Point the browser to a proxy endpoint</h3>
<p>Instead of sending telemetry directly to Elastic from the browser, configure the browser SDK to send OTLP traffic to a path on your own app origin, for example <code>/otlp</code>.
This gives you a same-origin endpoint from the browser's perspective and simplifies CORS handling.</p>
<pre><code class="language-javascript">startBrowserSdk({
  serviceName: 'frontend-web-app',
  otlpEndpoint: '/otlp',
});
</code></pre>
<p>Your reverse proxy will then forward <code>/otlp</code> to Elastic Managed OTLP or your EDOT / OpenTelemetry Collector.</p>
<h3>Step 4: Configure the reverse proxy</h3>
<p>Your reverse proxy should do three things:</p>
<ol>
<li>forward OTLP requests to the real backend endpoint (e.g., Elastic Managed OTLP endpoint)</li>
<li>inject the <code>Authorization</code> header</li>
<li>handle CORS and preflight requests if needed</li>
</ol>
<p>Here is an example NGINX-style configuration sketch:</p>
<pre><code class="language-nginx">location /otlp/ {
    add_header Access-Control-Allow-Origin https://your-app.example.com;
    add_header Access-Control-Allow-Headers Content-Type,Authorization;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
    add_header Access-Control-Allow-Credentials true;
    if ($request_method = OPTIONS) {
        return 204;
    }
    proxy_pass https://your-otlp-endpoint/;
    proxy_set_header Authorization &quot;ApiKey YOUR_SERVER_SIDE_KEY&quot;;
    proxy_set_header Host your-otlp-endpoint;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
</code></pre>
<p>Adjust this to match your environment.</p>
<p><strong>Important security note:</strong> Keep the API key on the server side only.
Do not expose it in JavaScript bundles, HTML source, browser dev tools, or public config files.</p>
<h3>Step 5: Make sure CORS is handled correctly</h3>
<p>If your browser app and OTLP destination are on different origins, the browser may block export requests unless CORS is configured correctly.</p>
<p>To ensure proper communication between your application and the OTLP endpoint, your proxy or backend should be configured to accept requests from your app’s origin.
It should also permit the necessary headers and HTTP methods.
Additionally, make sure it can correctly handle preflight OPTIONS requests required by CORS.
A same-origin proxy is often the easiest approach.</p>
<h3>Step 6: Add a small test page</h3>
<p>To verify that instrumentation is working, open or create a page that loads in the browser, allows you to click a button, and triggers a fetch request to a backend endpoint.
Here is a minimal example:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;EDOT Browser Demo&lt;/title&gt;
    &lt;script src=&quot;/assets/elastic-otel-browser.min.js&quot;&gt;&lt;/script&gt;
    &lt;script&gt;
      startBrowserSdk({
        serviceName: 'frontend-web-app',
        otlpEndpoint: '/otlp',
      });
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;EDOT Browser Demo&lt;/h1&gt;
    &lt;button id=&quot;loadData&quot;&gt;Load data&lt;/button&gt;
    &lt;script type=&quot;module&quot;&gt;
      document.getElementById('loadData').addEventListener('click', async () =&gt; {
        const response = await fetch('/api/demo');
        const data = await response.json();
        console.log(data);
      });
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<h3>Step 7: Start the app and generate traffic</h3>
<p>To begin generating telemetry, start your application and open it in a browser.
Once the application is running, load the page, click the button several times, and, if your app includes multiple pages, navigate between them.
Also, perform any actions that trigger XHR or fetch requests to ensure the instrumentation captures typical user interactions.</p>
<p>At this point, EDOT Browser should be collecting and exporting telemetry.</p>
<h3>Step 8: Verify the browser is sending telemetry</h3>
<p>Before checking Kibana, verify the browser is actually making OTLP requests.
Open browser developer tools and inspect the <strong>Network</strong> tab.</p>
<p>Look for requests to your configured endpoint, such as:</p>
<pre><code>/otlp/v1/traces
/otlp/v1/metrics
/otlp/v1/logs
</code></pre>
<p>When verifying that the browser is sending telemetry, make sure that requests are actually being made to the expected endpoint.
Check that responses are successful and confirm there are no CORS issues.
Look out for any 401 or 403 authentication errors, and ensure that your proxy is correctly forwarding OTLP traffic.
If you encounter failing requests, it is often due to an incorrect OTLP endpoint, missing authentication headers on the proxy, or a misconfigured CORS policy.
Other common causes include mismatched proxy paths or initializing the SDK too late or incorrectly within your application.</p>
<h2>Browser telemetry in Kibana</h2>
<p>EDOT Browser captures browser-side telemetry including document load events, user interactions, network requests, and Core Web Vitals.
In Kibana, this data typically appears as browser-originated spans, grouped interaction spans, correlated frontend and backend traces, and browser performance-related metrics.
These insights are especially helpful for understanding slow frontend interactions, identifying slow backend calls triggered by the UI, spotting user-visible performance issues, and analyzing end-to-end request behavior.</p>
<p>Once the browser telemetry is flowing, you can use the curated UIs and dashboards in Kibana or use <code>ES|QL</code>, Dashboarding, Alerting and other Elastic Observability features to analyze the data.</p>
<p>In this example we used a very simple web application (<code>play-app</code>) with a Node.js backend (<code>play-backend</code>).</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/edot-browser-rum/service-map.jpg" alt="Service Map" /></p>
<p>The service map shows the dependency between the browser app and backend service.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/edot-browser-rum/service-overview.jpg" alt="Service Overview" /></p>
<p>The service overview page provides a high-level view of the service, including typical RED metrics (i.e. rate, errors, duration) as well as an overview on the app's events / transactions (such as document load and click events).</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/edot-browser-rum/trace-waterfall.jpg" alt="Trace Waterfall" /></p>
<p>In the trace waterfall view, you can see the full trace of the browser request and the backend response.</p>
<p>In addition to the APM views, users can install the <code>RUM OpenTelemetry</code> content pack that comes with a ready-to-use dashboard for browser telemetry and web vitals.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/edot-browser-rum/dashboard-overview.jpg" alt="Dashboard" /></p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/edot-browser-rum/web-vitals.jpg" alt="Web Vitals" /></p>
<h2>Common troubleshooting tips</h2>
<p>If you do not see data in Kibana, check the following:</p>
<ol>
<li><strong>Is the SDK initialized?</strong> Make sure EDOT Browser is actually loaded and initialized in the page.</li>
<li><strong>Is it initialized early enough?</strong> Late initialization can miss important startup telemetry.</li>
<li><strong>Is the OTLP endpoint correct?</strong> Verify the browser is sending to the expected path.</li>
<li><strong>Is the proxy forwarding correctly?</strong> Confirm the proxy target URL is correct.</li>
<li><strong>Is authentication being injected?</strong> Managed OTLP or the Collector may require an <code>Authorization</code> header.</li>
<li><strong>Is CORS blocking requests?</strong> Look for browser console or network errors related to cross-origin requests.</li>
<li><strong>Is another browser agent also installed?</strong> Do not run EDOT Browser alongside another APM or RUM browser agent.</li>
</ol>
<h2>Limitations to keep in mind</h2>
<p>The following are not currently available in EDOT Browser:</p>
<ul>
<li>full feature parity with classic Elastic APM browser agents</li>
<li>migration tooling from classic agents</li>
</ul>
<p>If you are moving from a classic Elastic RUM agent, plan for testing and manual validation.</p>
<h2>Summary</h2>
<p>This article covered the basics of instrumenting a web application with EDOT Browser and viewing the data in Kibana.
For a more detailed guide, please refer to the <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/browser">EDOT Browser reference documentation</a>.</p>
]]></content:encoded>
            <category>observability-labs</category>
            <enclosure url="https://www.elastic.co/observability-labs/assets/images/edot-browser-rum/header.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>