---
title: Monitoring Resource Performance
description: >-
  Track web resource performance including XHR, Fetch, images, CSS, and
  JavaScript assets with detailed timing data and provider identification.
breadcrumbs: >-
  Docs > RUM & Session Replay > Application Monitoring > RUM Browser Monitoring
  > Monitoring Resource Performance
---

# Monitoring Resource Performance

The RUM Browser SDK collects resources and assets for every RUM view (page load): [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) (XHRs) and Fetch requests, but also images, CSS files, JavaScript assets, and font files. A RUM Resource event is generated for each one of them, with detailed timings and metadata.

RUM Resources inherit from all the context related to the active RUM View at the time of collection.

## Early data collection{% #early-data-collection %}

The `trackEarlyRequests` SDK init parameter allows you to capture:

- Unhandled rejections and uncaught exception errors when the SDK is first evaluated.
- XHR and Fetch requests regardless of SDK loading status.

To enable early data collection, set `trackEarlyRequests` to `true` in your Browser SDK initialization script. This feature requires Browser SDK v6.21.0+.

{% alert level="danger" %}
If you are using `beforeSend` with `trackEarlyRequests` enabled, properties associated with the request can be undefined.
{% /alert %}

For example:

```javascript
window.DD_RUM.init({
  ...
  beforeSend(event, eventContext) {
    if (event.type === 'resource' && event.resource.type === 'xhr') {
      eventContext.xhr.response // This might now break because `eventContext.xhr` is not necessarily defined
    }
  }
})
```

## Link RUM Resources to APM traces{% #link-rum-resources-to-apm-traces %}

To get even more complete, end-to-end visibility into requests as they move across layers of your stack, connect your RUM data with corresponding backend traces. This enables you to:

- Locate backend problems that resulted in a user-facing error.
- Identify the extent to which users are affected by an issue within your stack.
- See complete end-to-end requests on the flame graphs, allowing you to seamlessly navigate between RUM and APM and back with precise context.

See [Connect RUM and Traces](https://docs.datadoghq.com/real_user_monitoring/correlate_with_other_telemetry/apm.md) for information about setting up this feature.

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/resource_performance_graph.a1952d9b936444a0e4318587ef2cfa6d.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/browser/resource_performance_graph.a1952d9b936444a0e4318587ef2cfa6d.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="APM Trace information for a RUM Resource" /%}

## Track GraphQL requests{% #track-graphql-requests %}

The Browser SDK can automatically enrich GraphQL requests with operation-specific metadata, making it easier to identify and debug individual operations in RUM.

### Setup{% #setup %}

Configure `allowedGraphQlUrls` during SDK initialization to specify which endpoints should be treated as GraphQL:

```javascript
import { datadogRum } from '@datadog/browser-rum'

datadogRum.init({
    applicationId: '<DATADOG_APPLICATION_ID>',
    clientToken: '<DATADOG_CLIENT_TOKEN>',
    site: 'datadoghq.com',
    allowedGraphQlUrls: [
        // String: matches any URL starting with the value
        "https://api.example.com/graphql",
        // RegExp: tests against the full URL
        /\/graphql$/,
        // Function: evaluates with the URL as parameter, returning true for a match
        (url) => url.includes("graphql")
    ]
})
```

### Advanced options{% #advanced-options %}

To collect additional data, use the extended configuration:

```javascript
datadogRum.init({
    allowedGraphQlUrls: [
        {
            match: "https://api.example.com/graphql",
            trackPayload: true,          // Include GraphQL query (limited to 32 KB)
            trackResponseErrors: true    // Capture GraphQL errors from responses
        }
    ]
})
```

For matching requests, the SDK automatically extracts operation type, operation name, variables, and optionally the GraphQL query payload and response errors. See [GraphQL attributes](https://docs.datadoghq.com/real_user_monitoring/application_monitoring/browser/data_collected.md#graphql-attributes) for the complete list of collected attributes.

**Note**: You can modify GraphQL variables in the [`beforeSend` callback](https://docs.datadoghq.com/real_user_monitoring/application_monitoring/browser/advanced_configuration.md#modify-the-content-of-a-rum-event) if needed (for example, to redact sensitive data).

## Resource timing attributes{% #resource-timing-attributes %}

Detailed network timing data for resources is collected from the Fetch and XHR native browser methods and from the [Performance Resource Timing API](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus#browser_compatibility).

| Attribute                      | Type           | Description                                                                                                                                   |
| ------------------------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `resource.duration`            | number         | Entire time spent loading the resource.                                                                                                       |
| `resource.size`                | number (bytes) | Resource size.                                                                                                                                |
| `resource.connect.duration`    | number (ns)    | Time spent establishing a connection to the server (connectEnd - connectStart).                                                               |
| `resource.ssl.duration`        | number (ns)    | Time spent for the TLS handshake. If the last request is not over HTTPS, this attribute does not appear (connectEnd - secureConnectionStart). |
| `resource.dns.duration`        | number (ns)    | Time spent resolving the DNS name of the last request (domainLookupEnd - domainLookupStart).                                                  |
| `resource.redirect.duration`   | number (ns)    | Time spent on subsequent HTTP requests (redirectEnd - redirectStart).                                                                         |
| `resource.first_byte.duration` | number (ns)    | Time spent waiting for the first byte of response to be received (responseStart - RequestStart).                                              |
| `resource.download.duration`   | number (ns)    | Time spent downloading the response (responseEnd - responseStart).                                                                            |

**Note**: If you are having trouble collecting detailed timing for some resources, see Cross origin resources.

## Resource attributes{% #resource-attributes %}

| Attribute                  | Type   | Description                                                                                       |
| -------------------------- | ------ | ------------------------------------------------------------------------------------------------- |
| `resource.type`            | string | The type of resource being collected (for example, `css`, `javascript`, `media`, `XHR`, `image`). |
| `resource.method`          | string | The HTTP method (for example `POST`, `GET`).                                                      |
| `resource.status_code`     | number | The response status code.                                                                         |
| `resource.url`             | string | The resource URL.                                                                                 |
| `resource.url_host`        | string | The host part of the URL.                                                                         |
| `resource.url_path`        | string | The path part of the URL.                                                                         |
| `resource.url_query`       | object | The query string parts of the URL decomposed as query params key/value attributes.                |
| `resource.url_scheme`      | string | The protocol name of the URL (HTTP or HTTPS).                                                     |
| `resource.provider.name`   | string | The resource provider name. Default is `unknown`.                                                 |
| `resource.provider.domain` | string | The resource provider domain.                                                                     |
| `resource.provider.type`   | string | The resource provider type (for example `first-party`, `cdn`, `ad`, `analytics`).                 |

**Note**: Some fields may not be available in all browsers. For example, `resource.status_code` is not available in Safari, see [Browser Compatibility](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus#browser_compatibility).

## Identify third-party resources{% #identify-third-party-resources %}

RUM infers the name and category of the resource provider from the resource URL host part. If the resource URL host matches the current page URL host, the category is set to `first party`. Otherwise, the category will be `cdn`, `analytics`, or `social` for example.

## Cross origin resources{% #cross-origin-resources %}

Certain resource timings and attributes are collected using the [Resource Timing API](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming). However, when a resource originates from a different URL than the current page (for example, a web application hosted on `www.example.com` loading resources from `static.example.com`), the browser's security policy restricts access to some of this information.

### Resource timings{% #resource-timings %}

To collect detailed resource timings, add the `Timing-Allow-Origin` HTTP response header to your cross-origin resources. For example, to grant access to the resource timing to any origin, use `Timing-Allow-Origin: *`. For more information about CORS, see [Cross-origin timing information](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Resource_timing#cross-origin_timing_information) in the MDN Web Docs.

### Resource status code{% #resource-status-code %}

To collect the resource status code, add the `Access-Control-Allow-Origin` HTTP response header and the `crossorigin` attribute to the relevant HTML tags to allow access to cross-origin resources. For example, to grant access to the resource status code to any origin, use `Access-Control-Allow-Origin: *` and add `crossorigin="anonymous"` to your HTML tags. For more information, see [`Access-Control-Allow-Origin` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Origin), and [`crossorigin` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) in the MDN Web Docs.

## Further Reading{% #further-reading %}

- [Real User Monitoring](https://www.datadoghq.com/blog/real-user-monitoring-with-datadog/)
- [Explore your views within Datadog](https://docs.datadoghq.com/real_user_monitoring/explorer.md)
- [Apply visualizations on your events](https://docs.datadoghq.com/real_user_monitoring/explorer/visualize.md)
- [RUM Dashboards](https://docs.datadoghq.com/real_user_monitoring/platform/dashboards.md)
