Blog

- December 15, 2015

Cross Origin Resource Sharing (CORS) allows us to use Web applications within browsers when domains aren’t the same. For example, a site with domain test.org wants to execute AJAX requests to a Web application with domain mydomain.org using HTTP.

Using CORS isn’t so simple especially when you face debugging difficulties. As a matter of fact, CORS can imply an additional OPTIONS request and error messages aren’t so explicit. Most of the time, errors correspond to a lack of required headers from the server. For such reasons, a good understanding of how this feature works is essential.

CORS is used in a lot of places and use cases. In Web development, it’s often necessary to split the front application from the server application for development reasons or to interact with a remote service.

The CORS mechanism is mainly implemented with the Web server but this has an impact on the client side if some headers are missing in responses.

Understanding CORS

With CORS, the remote Web application (here the one with domain mydomain.org) chooses if the request can be served. The CORS specification distinguishes two distinct use cases:

  • Simple requests. This use case applies if we use HTTP GET, HEAD and POST methods. In the case of POST methods, only content types with the following values are supported: text/plain, application/x-www-form-urlencoded and multipart/form-data.
  • Preflighted requests. When the ‘simple requests’ use case doesn’t apply, a first request (with the HTTP OPTIONS method) is made to check what can be done in the context of cross-domain requests.

Notice that if you add authentication to the request using the Authentication header, simple requests automatically become preflighted ones.

Client and server exchange a set of headers to specify behaviors regarding cross-domain requests. Let’s have a look at them now. We will then describe how they are used in both use cases.

  • Origin: this header is used by the client to specify which domain the request is executed from. The server uses this hint to authorize, or not, the cross-domain request.
  • Access-Control-Request-Method: with in the context of preflighted requests, the OPTIONS request sends this header to check if the target method is allowed in the context of cross-domain requests.
  • Access-Control-Request-Headers: with in the context of preflighted requests, the OPTIONS request sends this header to check if headers are allowed for the target method in the context of cross-domain requests.
  • Access-Control-Allow-Credentials: this specifies if credentials are supported for cross-domain requests.
  • Access-Control-Allow-Methods: the server uses this header to tell which headers are authorized in the context of the request. This is typically used in the context of preflighted requests.
  • Access-Control-Allow-Origin: the server uses this header to tell which domains are authorized for the request.
  • Access-Control-Allow-Headers: the server uses this header to tell which headers are authorized in the context of the request. This is typically used in the context of preflighted requests.

Simple requests

Where simple requests are concerned, the request is executed against the other domain. If the remote resource supports cross domains, the response is directly sent back. Otherwise an error occurs.

simple-request

Here is a sample content for a cross-domain request:

GET /myresource/ HTTP/1.1
Host: mydomain.org
(...)
Referer: http://test.org/example.html
Origin: http://test.org

And the corresponding response:

HTTP/1.1 200 OK
(...)
Access-Control-Allow-Origin: *
Content-Type: application/json

[JSON Data]

Preflighted requests

In the case of preflighted requests, this corresponds to a negotiation between the caller and the Web application based on HTTP headers. It consists of two phases:

The browser first executes an OPTIONS request with the same URL as the target request to check that it has the rights to execute the request. This OPTIONS request returns headers that identify what is possible to do for the URL. If rights match, the browser executes the request.

preflighted-request

Here is an example exchange with a first cross-domain request (the OPTIONS one):

OPTION /myresource/ HTTP/1.1
Host: mydomain.org
(...)
Origin: http://test.org
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type,accept

And the corresponding response:

HTTP/1.1 200 OK
(...)
Access-Control-Allow-Origin: http://test.org
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: content-type,accept
Access-Control-Max-Age: 1728000

Since the OPTIONS pre-request succeeds, the browser will then send the actual request:

POST /myresource/ HTTP/1.1
Host: mydomain.org
Content-type: application/json
Accept: application/json
(...)
Referer: http://test.org/example.html
Origin: http://test.org

[JSON Data]

And the corresponding response:

HTTP/1.1 200 OK
(...)
Access-Control-Allow-Origin: *
Content-Type: application/json

[JSON Data]

This processing is completely transparent for the caller but we can have hints of what actually happens using the development tools of the browser, for example Firebug within Firefox. With the latter, we can use the Network tab to check which calls are executed and which CORS headers are exchanged.

An important comment. You must take into account that, when executing CORS request containing security, i.e. an Authorization header, the OPTIONS request won’t contain it. So you need to be careful regarding security when handling the first OPTIONS requests of preflighted ones. As a matter of fact, no authentication check can be done at this level.

Use within browsers

When implementing applications within browsers using JavaScript, it’s common to interact with services that aren’t hosted on the same domain. Several solutions are usable like JSONP, but also CORS. In this section, we will describe how to use this feature within commonly used tools.

Low-level API

XHR corresponds to the low-level API to implement AJAX calls within JavaScript applications in browsers. Regarding CORS, there is nothing to do unless you want to use credentials with the withCredentials property. It’s mainly if you deal with cookies.

In fact, if the browser detects that the domain of the page is different from the AJAX request that is about to be sent, it automatically adds the Origin header. The consequence is to enable CORS support within the response if the Web service supports this technology.

Here is a sample of a simple CORS request:

var req = new XMLHttpRequest();

req.open('GET', 'https://maptestapi.apispark.net/v1/maps/', true);
req.onreadystatechange = function() {
    if (req.readyState === 4) {
        console.log(req.responseText);
    }
};
req.setRequestHeader('Accept', 'application/json');
req.send();

This will result in the following request in the browser:

console1

Here is a sample of a preflighted CORS request:

var req = new XMLHttpRequest();

req.open('PUT', 'https://maptestapi.apispark.net/v1/maps/4', true);
req.onreadystatechange = function() {
    if (req.readyState === 4) {
        console.log(req.responseText);
    }
};
req.setRequestHeader('Content-type', 'application/json');
req.send('{ "id": "4", "name": "Meteorites", "type": "d3js", (…)');

This will result in the following request in the browser:

console2

As described above, CORS is natively supported by the JavaScript XMLHttpRequest object. We can notice that old Internet Explorer versions (7 and 8) use a dedicated object for XDomainRequest requests.

That being said, if you want to send credentials managed by the browser (Access Control) within CORS requests, you need to enable the credentials support of XHR using the withCredentials attribute. By default, the feature is disabled.

var req = new XMLHttpRequest();
(...)
req.withCredentials = true;

Notice that you don’t need to use this attribute if you build the Authentication header by yourself.

Libraries and frameworks

The aim of JavaScript libraries and frameworks regarding AJAX is to provide an abstraction level upon browser specificities, an API that is common and works in every browser. That means that using CORS is transparent.

JQuery provides a way to configure the withCredentials attribute previously discussed, as described below:

$.ajax({
    type: "POST",
    data: {},
    dataType: 'json',
    xhrFields: {
       withCredentials: true
    }
});

With AngularJS, there is nothing to do.

Note that on the web you can find content telling you that you need to set the defaults.useXDomain property of the $httpProvider object to true or to delete the X-Requested-With header. This may have been true in the past but it’s not the case anymore.

Error detection

To detect CORS problems, you obviously need to enable debugging tools in your browser and especially the JavaScript console. For example, in Chrome, if the server doesn’t include CORS headers in the response, you will see this error for a simple request:

console3

And this one for a preflighted one:

console4

Using the Network tab, you will see that CORS headers aren’t present in the response of the OPTIONS request:

console5

Errors can be more subtle since the content of CORS headers could possibly be incorrect. For example, if you try to call a HTTP method that didn’t return Access-Control-Allow-Methods.

Another source of errors is that credentials aren’t sent within the OPTIONS request (Authorization header). When implementing CORS on the server side, checking credentials musn’t apply for this request but only for target ones.

console6

Implementing CORS on the server-side

As you can see, although CORS aims to make cross domain requests possible within browsers, a big part of the work must be done within server-side applications by setting the right headers in response.

In this section, we will describe how to implement CORS into such applications for different technologies. Let’s start with the Restlet framework.

Implementing CORS within Restlet Framework

Restlet Framework provides a convenient built-in support of CORS through a dedicated service called CorsService, which can be simply initialized, configured and finally added in the list of application services.

Under the hood, Restlet Framework will leverage this service to add and configure a CORS filter that will manage preflighted requests and the CORS headers. Such processing detects the presence of the Origin header to enable this support.

The following properties are supported for this service:

  • allowedHeaders: the modifiable set of headers allowed by the request on the current resource. This property maps to the Access-Control-Allow-Headers header.
  • allowedOrigins: returns the URI an origin server, allows for the requested resource.
  • exposedHeaders: returns a modifiable whitelist of headers an origin server allows for the requested resource. This property maps to the Access-Control-Expose-Headers header.
  • allowedCredentials: if true, adds Access-Control-Allow-Credentials header.
  • allowingAllRequestedHeaders: if true, indicates that the value of Access-Control-Request-Headers request header will be copied into the Access-Control-Allow-Headers response header.
  • skippingResourceForCorsOptions: if true, the filter does not call the server resource for OPTIONS method of CORS request and set Access-Control-Allow-Methods header with the default methods.

The following code describes how to configure this service within your Restlet Framework application class:

public class MyApplication extends Application {
  public MyApplication() {
    CorsService corsService = new CorsService();         
    corsService.setAllowedOrigins(new HashSet(Arrays.asList("*")));
    corsService.setAllowedCredentials(true);
    getServices().add(corsService);
  }

  public Restlet createInboundRoot() {
    (...)
  }
}

Node

Several Web frameworks are available for Node. One of the most popular is Express. Here we will describe how to support CORS for Web applications implemented with this tool.

The first step consists in installing the cors tool. The latter corresponds to a Connect/Express middleware that can be used to enable CORS with various options.

$ npm install cors

This tool allows to automatically configure CORS response headers. After having obtained an instance on the module using the require function, you can register it in the request processing chain using the use method:

var express = require('express')
  , cors = require('cors')
  , app = express();

app.use(cors());

(...)

This can also be defined at the route definition level as second parameter:

var express = require('express')
  , cors = require('cors')
  , app = express();

app.get('/products/:id', cors(), function(req, res, next) {
  (...)
});

The support of preflighted requests can be done by leveraging Express’ routing. The first approach is to define it globally with the * character. Every request will be handled by the cors() function.

var express = require('express')
  , cors = require('cors')
  , app = express();

app.options('*', cors());

This can be handled per route. In this case, it’s a bit more restrictive since this must be defined for each route:

var express = require('express')
  , cors = require('cors')
  , app = express();

app.options('/products/:id', cors());
app.del('/products/:id', cors(), function(req, res, next) {
  (...)
});

When instantiating the cors object, you can give it a map of attributes. The available ones are described below:

  • origin: Configures the Access-Control-Allow-Origin CORS header. Expects a string (e.g. http://example.com). Set to true to reflect the request origin, as defined by req.header('Origin'). Set to false to disable CORS.
  • methods: Configures the Access-Control-Allow-Methods CORS header. Expects a comma-delimited string (e.g. 'GET,PUT,POST') or an array (e.g. ['GET', 'PUT', 'POST']).
  • allowedHeaders: Configures the Access-Control-Allow-Headers CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (e.g. ['Content-Type', 'Authorization']). If not specified, defaults to reflecting the headers specified in the request’s Access-Control-Request-Headers header.
  • exposedHeaders: Configures the Access-Control-Expose-Headers CORS header. Expects a comma-delimited string (e.g., Content-Range,X-Content-Range) or an array (e.g. ['Content-Range', 'X-Content-Range']). If not specified, no custom headers are exposed.
  • credentials: Configures the Access-Control-Allow-Credentials CORS header. Set to true to pass the header, otherwise it is omitted.
  • maxAge: Configures the Access-Control-Allow-Max-Age CORS header. Set to integer to pass the header, otherwise it is omitted.
  • preflightContinue: Pass the CORS preflight response to the next handler.

API Cloud

API Cloud is a comprehensive platform to create, deploy Web APIs (RESTful services) easily and through an online IDE. Web APIs created with this tool support CORS out of the box for both simple and preflighted requests.

Here is what you get within the response when calling a Web API hosted by API Cloud:

console7

Conclusion

As you can see, using CORS isn’t so tricky but when things go wrong, the errors triggered by the technology can be weird and not intuitive at the browser level. We need to keep an eye on the CORS headers and corresponding values sent back when implementing server resources that support cross-domain requests.

When implementing the technology, a good understanding of what happens is required as it does contain some error-prone pitfalls. Forgetting to add a header or a header value can trigger errors that prove difficult to solve.

Fortunately, certain tools provide an abstraction of these exchanges and headers making it easier to provide CORS out of the box.

More: Restlet founder Jerome Louvel discusses API DevOps

  • Opal

    The description for “Access-Control-Allow-Methods” is invalid – the same as for “Access-Control-Allow-Headers”.

    • Thanks very much of pointing this out! I fixed these typos in the post 😉

  • Vishal Singla

    could not able to import CorsService class in my project.

  • Pingback: Testing Web APIs using DHC by Restlet | Restlet - We Know About APIs()

  • Pingback: API Testing – Testing Web APIs using DHC by Restlet | Restlet - We Know About APIs()

  • Tausif

    very informative. Thanks for explaining it in very easy manner.

  • Senthil Kumar
  • vik

    You can enable CORS in web servers like apache or nginx. I would like to know where would you place it. In service you are programming or in the server which in front of your services?

    I would tend to place it in the service along with other header’s I am providing in the service. I can test it as developing better and have control of it and would not need to ask the ops team.
    And all headers belong to the communication of my service. The web server would add header information if it is such as load balancing.

    Maybe somebody has more arguments for one or the other approach and links to articles?

  • Savi Goyal

    Thanks a lot nice describe
    can u help me in this

    Cross-Origin
    Request Blocked: The Same Origin Policy disallows reading the remote
    resource at https://moodeapp.com/app/social/signUp. (Reason: missing
    token ‘access-control-allow-methods’ in CORS header
    ‘Access-Control-Allow-Headers’ from CORS preflight channel).

  • Mohamed Eloifi

    ??