Professor Sloth

Free web performance master class

Learn about web performance and how to make your site faster, delivered straight to your inbox.

How to Monitor Cross-Origin Resource Performance

How to Monitor Cross-Origin Resource Performance

Browsers provide detailed performance information about every resource a webpage loads. Most of this information is hidden during cross-origin requests however. This is a common problem since pages often load content from a variety of origins. Pages can access cross-origin timing information if an additional header is added to cross-origin responses.

For security reasons, the browser’s Resource Timing API does not allow access to detailed measurements when the resource is loaded from a different origin than the page (called a cross-origin request). A request is considered cross-origin if the resource’s origin is in any way different than the page itself. For example, a resource hosted at https://cdn.example.com is a cross-origin request if the page loaded from https://example.com.

Web performance monitoring services like Request Metrics work best when they have access to all resource timings which provides the most context and visibility into page performance.

The Timing-Allow-Origin Response Header

An origin can allow access to all resource timings by including a properly configured Timing-Allow-Origin header in all responses. Access is controlled on a per page origin basis.

Configuring your webserver, service or CDN to return the header will vary depending on the software or service being used. The important thing is return the header in all HTTP responses from your service.

Once the header has been added to your configuration, test it with our Online Timing-Allow-Origin Checker.

Allow Specific Origins

Specific page origins can be given access to timings by returning them in a comma separated list.

Timing-Allow-Origin: https://example.com, http://other-example.com
Allow timings for specific page origins

Allow All Origins

In cases like CDN’s where a domain is accessed from many different websites, a wildcard can be used instead of listing each website’s domain.

Example PerformanceResourceTiming Data

The Resource Timing API returns a PerformanceResourceTiming object for each resource requested by the page.

Zeroed Timings Because of Cross-Origin Resource

By default, most PerformanceResourceTiming properties are zeroed out when the resource is from another origin:

{
  "name": "https://cdn.example.com/path/script.js",
  "entryType": "resource",
  "startTime": 267,
  "duration": 189,
  "initiatorType": "script",
  "nextHopProtocol": "",      // * Blank because cross-origin
  "workerStart": 0,
  "redirectStart": 0,         // * Zero because cross-origin
  "redirectEnd": 0,           // * Zero because cross-origin
  "fetchStart": 267,
  "domainLookupStart": 0,     // * Zero because cross-origin
  "domainLookupEnd": 0,       // * Zero because cross-origin
  "connectStart": 0,          // * Zero because cross-origin
  "connectEnd": 0,            // * Zero because cross-origin
  "secureConnectionStart": 0, // * Zero because cross-origin
  "requestStart": 0,          // * Zero because cross-origin
  "responseStart": 0,         // * Zero because cross-origin
  "responseEnd": 456,
  "transferSize": 0,          // * Zero because cross-origin
  "encodedBodySize": 0,       // * Zero because cross-origin
  "decodedBodySize": 0,       // * Zero because cross-origin
  "serverTiming": []
}
PerformanceResourceTiming entry from a third party domain with no 'Timing-Allow-Origin' header

Populated Timings With Timing-Allow-Origin Header

With the the Timing-Allow-Origin header, all properties are populated just as they would be if the resource had been loaded from the page origin.

{
  "name": "https://cdn.example.com/path/script.js",
  "entryType": "resource",
  "startTime": 860,
  "duration": 176,
  "initiatorType": "script",
  "nextHopProtocol": "h2",
  "workerStart": 0,
  "redirectStart": 0,
  "redirectEnd": 0,
  "fetchStart": 860,
  "domainLookupStart": 863,
  "domainLookupEnd": 888,
  "connectStart": 889,
  "connectEnd": 940,
  "secureConnectionStart": 910,
  "requestStart": 940,
  "responseStart": 1032,
  "responseEnd": 1036,
  "transferSize": 13609,
  "encodedBodySize": 12253,
  "decodedBodySize": 38659,
  "serverTiming": []
}
PerformanceResourceTiming entry from a third party domain with a valid 'Timing-Allow-Origin' header

List of All Hidden PerformanceResourceTiming Properties

Many important resource metrics are hidden by default during cross-origin requests:

  • Timings: redirectStart, redirectEnd, domainLookupStart, domainLookupEnd, connectStart, connectEnd, secureConnectionStart, requestStart, responseStart
  • Sizes: transferSize, encodedBodySize, decodedBodySize
  • Protocol: nextHopProtocol

Timing-Allow-Origin vs Access-Control-Allow-Origin Headers

Both headers contain lists of origins, but they have very different uses. Access-Control-Allow-Origin is part of CORS and controls whether a page’s Javascript has access a cross-origin resource. Timing-Allow-Origin has no impact on access to the resource, only the timing information after the resource has been loaded by the browser.

Jordan Griffin
VP Engineering Request Metrics