Cross-Origin Resource Sharing (CORS)

In this tutorial, we’ll understand an important HTTP concept known as Cross-Origin Resource Sharing or CORS. After exploring the concepts, we’ll look at a real-world example and do a walk through using Chrome.

What is Cross-Origin Resource Sharing or CORS?

Cross-Origin Resource Sharing (CORS) is standard or policy that allows a 3rd party server to specify whether or not it can be accessed by a web page running in a browser that was fetched from another domain.

For example, suppose a web page is served from domain https://a-domain.com. Embedded JavaScript on that page wants to make HTTP POST request using XMLHttpRequest to a 3rd party server at https://some-analytics-site.com/. This outgoing request falls under the jurisdiction of CORS.

CORS is a specification or a standard that is implemented by all modern browsers. It was devised to limit “unsafe HTTP requests that can be automatically launched toward destinations that differ from the running application’s origin”.

Are all cross-origin requests subject to CORS?

No. Cross-origin images, stylesheets, scripts, iframes, and videos that a web page embeds from other sites are not subject to CORS, mainly because they don’t pose a security threat.

Generally speaking, JavaScript AJAX calls (XMLHttpRequest/Fetch) to external domains (other than the one that served the web page) fall under CORS.

Here’s a list of all requests that may be subject to CORS:

  • Cross-origin XMLHttpRequest or Fetch API
  • Web Fonts (@font-face). This allows servers to specify what other servers are allowed to download these.
  • WebGL textures.
  • Images/video frames drawn to a Canvas using the drawImage() method.
  • CSS Shapes from images.

How CORS work?

Put simply, a CORS-compatible browser asks the other server if it would permit the web page to make requests to it. If the server allows, the request is made. Otherwise, browser blocks the request. The JavaScript making the request gets a generic “error occurred” message by the browser and it can’t tell if the request was blocked by CORS. The only way to see CORS error is by reviewing browser logs.

CORS spec is implemented by all modern web browsers. It works by adding new HTTP headers to outgoing requests to let servers specify if they’d allow the request from web page or not. For requests that can use side-effects on server (HTTP POST), the specification requires another pre-flight request to be first sent, before sending the actual request.

Now let’s take a more in-depth look at how CORS work.

CORS Example

Let’s look at a real example. This webpage that you are reading (and all other webpages at CodeAhoy.com) uses an embedded JavaScript from Unlaunch to control the launch of new features (Unlaunch is a popular feature flagging tool.) The Unlaunch JavaScript makes a “HTTP POST” call when the page is loaded with JSON body to ask the server to check for feature flags.

Let’s look at CORS in action in Chrome. (If you are using Chrome, you can also follow these examples using Chrome Inspector on this webpage.)

1. CORS Pre-flight request

The Unlaunch JavaScript on this webpage makes a non-standard HTTP POST request to api.unlaunch.io, because:

  • Content-type is application/json
  • It is setting an extra HTTP header: x-api-key.

This triggers CORS in Chrome to first send a pre-flight request to check if the Unlaunch server accepts HTTP POST with custom headers. A pre-flight is simply an “HTTP OPTIONS” request asks the server if it would accept the upcoming request. Pre-flight requests can be found in Chrome’s “Network” tab, under “Other”.

Pre-flight CORS request in Chrome

Pre-flight CORS request in Chrome.

In the image we above, we can see that the Chrome sent a pre-flight request to api.unlaunch.io asking if it accepts HTTP POST and with custom content-type and x-api-key headers. The server specified that it does by sending the access-control-allow-methods: POST header in its response.

Access-Control-Allow-Origin: https://codeahoy.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: content-type, x-api-key

The browser can now proceed to sending the actual request.

2. Make the Cross-Origin POST Request

The actual request is controlled by CORS using two headers:

  • Origin in the request. Browser puts the domain that served the webpage to tell the server that it’s making the request on domain’s behalf.
  • Access-Control-Allow-Origin in the response. The servers way of telling if it accepts requests from that domain or not.

Here’s the request in Chrome (All AJAX requests in Chrome Inspector can be found in “Network” tab under “Fetch/XHR”.)

CORS request in Chrome

CORS request in Chrome.

In the response, you can see that the server at Unlaunch indicates that it accepts requests from codeahoy.com

Calls that Trigger CORS Pre-flight Request

The following requests will not trigger a CORS pre-flight:

  • HTTP GET
  • HTTP Head

HTTP POST and CORS

Not all HTTP POST requests from JavaScript triggers a pre-flight request. POST requests that meet the following criteria will not trigger pre-flight:

  • Contains only the following headers:
    • Accept
    • Accept-Language
    • Content-Language
    • Range

In addition, the content-type header is allowed by only with the following values:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

Debugging / Testing CORS Requests

Web application developers often find themselves running into CORS related issue. If you are debugging or troubleshooting, you could use Chrome extensions to force add Access-Control-Allow-Origin: * to the response header so browser would allow requests to be made. These extensions essentially disabled CORS feature in your browser so use these carefully and only for troubleshooting your own applications. Here’s an extension you can use to bypass CORS in Chrome:

You can also use Postman to test CORS and see if the server is returning the required headers or not.

(C) 2021 CodeAhoy.com. You can use the material on your site but you must link back to this page.



Speak Your Mind