What is the impact of CORS on performance monitoring?

Introduction

CORS, which stands for Cross-Origin Resource Sharing, has an impact on performance monitoring. The goal of this article is to explain what CORS consists in, why it is used, and how it impacts the way you can monitor digital services performance.

Modern web applications make extensive use of third-party resources. Examples are the use of CDNs to better deliver content like images and stylesheets, or third-party services resources that are called through APIs. So one webpage can be the result of a multitude of requests to different locations called origins. This is especially true for SPAs (Single Page Applications) that mainly rely on calls to API backends.

A web content origin is defined by the scheme (protocol), host (domain), and port of the URL used to access it.

same origins vs different origins

So as soon as the browser performs requests to different origins, CORS comes into play.

Let’s discuss why CORS has an impact on performance monitoring.

What is CORS – Cross-Origin Resource Sharing?

CORS is a mechanism that allows a server to indicate any other origins than its own from which a browser should permit loading of resources.

As such, this is mainly a security mechanism that is enforced at the browser level by the server. It is used to allow requests made on your behalf and at the same time block some requests made by potential malicious JavaScripts.

Before the CORS standardization, all calls to external origins (fetch or ajax calls) were blocked by the Same-origin Policy.

The following picture shows a simple example of a webpage that contains two images and some styling.

The first HTTP GET request (GET /) determines the content origin (domain-a.com).

Rendering the page correctly requires the download of a stylesheet (layout.css) that is located on a third-party service via a cross-origin request (domain-b.com).

Finally, one image (image-1) is stored on the origin server, while the second (image-2) is stored on a third domain (domain-c.com).

So in this small example, getting the stylesheet as well as the image-2 is performed through cross-origin requests.

How does CORS work?

So far, you have seen that a server can provide the browser with a list of origins from which it can request content. The browser typically requests this content through ajax or fetch calls triggered by JavaScripts.

In the previous example, domain-b.com and domain-c.com servers should allow the browser to fetch resources initiated from a previous connection to the domain-a.com (e.g. a JavaScript downloaded from the domain-a.com server and executed on the browser).

But how does a server grant this permission to a browser?

It does this through specific HTTP headers.

CORS in HTTP responses

The server grants the browser permission to access resources from other origins. It does this by using the Access-Control-Allow-Origin HTTP header.

For example, you can allow requesting resources from any origin as follows: Access-Control-Allow-Origin: *

You can also narrow down to a specific origin: Access-Control-Allow-Origin: https://example.com

CORS in HTTP requests

There are two types of HTTP requests in a CORS context: “simple” and “preflight”.

The “simple” request

In the following example, the client sends a GET request to the home page of the website example2.com.

This request is coming from the https://example.com, which is specified by the Origin request header.

In response, the server sends back an Access-Control-Allow-Origin header with the value *, meaning that the resource can be accessed by any origin.

Simple CORS request

The “preflight” request

You will often encounter the preflight type of request when the browser intends to use HTTP methods like PUT, POST or DELETE. These methods obviously represent a bigger security risk as they typically modify web content on the server.

Unlike “simple” requests, in “preflight” requests, the browser first sends an HTTP request using the OPTION method to determine if the actual request is safe to send. In other worlds, this preflight request checks whether the main request will be allowed or not.

Seeing that the OPTION method cannot modify the resource itself, this is a safe method to use.

Preflight request

This example shows the basic process:

  1. The client sends a first preflight request using the POST method. Among others, this request includes the following information:
    • The origin of the request (https://example.com)
    • The HTTP method the browser will use in the main request (POST as mentioned by the Access-Control-Request-Method header)
    • The HTTP header the browser will use in the main request (X-PINGOTHER as mentioned by the Access-Control-Request-Headers header)
  2. The server responses back to the browser, confirming that the main request will be allowed. For this, the response includes the following headers:
    • Access-Control-Allow-Origin as previously explained
    • Access-Control-Allow-Methods that provides the types of HTTP methods that will be allowed
    • Access-Control-Allow-Headers that provides the list of types of headers that will be accessible by the main request (X-PINGOTHER and Content-Type in this example)
  3. Based on the preflight response, the client performs the main request
  4. As the request allowance has been verified previously, the server sends a response to this main request. Note that the Access-Control-Allow-Origin header is present in all server responses.

The following site provides codes to implement CORS on different platforms.

What does CORS mean for your performance monitoring?

Now that you know why CORS exists and how it works, let’s have a look at the way performance metrics are reported in this context.

Major Real User Monitoring solutions use the W3C Time Navigation API as a basis for collecting performance metrics:

The following article provides more details about the page load process.

The problem that cross-origin resources introduce here is that they only expose timestamps for the following attributes by default:

  • startTime (will equal fetchStart)
  • fetchStart
  • responseEnd
  • duration

This is to protect users’ privacy (so an attacker cannot load random URLs to see where you’ve been).

This means that all of the following attributes will be “Null” for cross-origin resources:

  • redirectStart
  • redirectEnd
  • domainLookupStart
  • domainLookupEnd
  • connectStart
  • connectEnd
  • secureConnectionStart
  • requestStart
  • responseStart

In addition, all size information will be “0” for cross-origin resources:

  • transferSize
  • encodedBodySize
  • decodedBodySize

Last but not the least, cross-origin resources that are redirected will have a startTime that only reflects the final resource — startTime will equal fetchStart instead of redirectStart. This means the time of any redirect(s) will be hidden from the resource timing.

So as you can see, using CORS has an impact on performance monitoring!

The solution to monitor the performance of CORS resources: enabling TAO (Time-Allow-Origin)

Luckily, if you control the domains you are fetching other resources from, you can bypass the default protection mechanism by sending a Timing-Allow-Origin header in HTTP responses, informing the browser that it is allowed to process performance metrics.

If you are serving any of your content from another domain, i.e. from a CDN, it is strongly recommended that you set this header for those responses. Thankfully, third-party libraries for widgets, ads, analytics etc, are starting to set this TAO header on their content.

In 2013, only about 1,2% of the total resources embedded on a few hundred thousand websites presented the Time Access Origin (TAO) header. In 2018, this represented about 15% of the total websites. So there is still a long way to go.

Finally, if you still want to get performance metrics from the W3C Time Navigation API, there are browser extensions out there that allow the browser to bypass any restriction. This is obviously to the detriment of security! So just consider this workaround as a temporary solution when you require specific degradations troubleshooting.

Your next steps on CORS performance monitoring

CORS is a security mechanism that is enforced at the browser level. If you control the different servers, make sure to allow cross-origins requests by adding the Access-Control-Allow-Origin header in HTTP responses in order to avoid errors in fetching resources.

Using CORS has an impact on performance monitoring. Monitoring performances of cross-origins requests is a challenge because the metrics are not reported by default.

If you use third-party services like CDNs, make sure they allow you to activate the Timing-Allow-Origin header in HTTP responses.

In case this is not possible but you really need to collect these performance metrics, temporarily enable specialized browser’s extensions that will disable this security protection.