Cross-Origin Resource Sharing (CORS) is an essential mechanism that enables communication between clients and servers, even if they are on different domains. Normally, JavaScript applications running in the browser can only access HTTP resources on the same domain that serves them. However, CORS provides a way to allow connections to other servers.

By default, certain resources like images, scripts, and styles can be loaded from different origins. However, requests made using XHR or Fetch to a different domain, subdomain, port, or protocol will fail unless the server implements a CORS policy. The same-origin policy also applies to loading web fonts, WebGL textures, and resources used in the Canvas API’s drawImage method.

One crucial use case for CORS is with ES Modules, which have been introduced in modern browsers. Without setting up a CORS policy on the server to allow third-party origins, the request will fail.

Here are two examples of failed requests due to CORS policy restrictions:

Fetch example: Fetch failed because of CORS policy

XHR example: XHR request failed because of CORS policy

This security restriction is in place to prevent malicious users from exploiting the web platform. However, if you have control over both the server and the client, it is necessary to configure them to allow communication with each other.

The implementation of CORS depends on your server-side stack. Fortunately, CORS has good browser support, with the exception of IE<10 as shown below: CORS browser support

Here’s an example of implementing CORS with Express, a popular Node.js framework:

const express = require('express')
const cors = require('cors')
const app = express()

app.get('/without-cors', (req, res, next) => {
    res.json({ msg: '😞 no CORS, no party!' })
})

const server = app.listen(3000, () => {
    console.log('Listening on port %s', server.address().port)
})

To enable CORS in Express, use the cors middleware package by requiring it and passing it as a middleware function to the endpoint request handler as shown:

const express = require('express')
const cors = require('cors')
const app = express()

app.get('/with-cors', cors(), (req, res, next) => {
    res.json({ msg: 'WHOAH with CORS it works! πŸ” πŸŽ‰' })
})

/* the rest of the app */

In this example, any request will be accepted by the server as cross-origin. To specify only specific origins, use the corsOptions object:

const cors = require('cors')

const corsOptions = {
    origin: 'https://yourdomain.com',
}

app.get('/products/:id', cors(corsOptions), (req, res, next) => {
    //...
})

Alternatively, you can configure the server to allow multiple origins by defining a whitelist array:

const whitelist = ['http://example1.com', 'http://example2.com']
const corsOptions = {
    origin: function (origin, callback) {
        if (whitelist.indexOf(origin) !== -1) {
            callback(null, true)
        } else {
            callback(new Error('Not allowed by CORS'))
        }
    },
}

Certain requests require a pre-approval phase called preflight. All GET requests fall into this category, along with some POST and HEAD requests. POST requests are subject to preflight requirements if they use one of the following Content-Types: application/x-www-form-urlencoded, multipart/form-data, or text/plain. All other requests must go through a preflight process, which involves the browser issuing an OPTIONS request to determine if it has permission to perform the action. The server responds to the preflight request with headers specifying allowed methods.

To handle preflight requests in Express, set up routes to handle the OPTIONS request:

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

// Allow OPTIONS on just one resource
app.options('/the/resource/you/request', cors())

// Allow OPTIONS on all resources
app.options('*', cors())

This concludes the introduction to Cross-Origin Resource Sharing (CORS), the mechanism that enables clients and servers to communicate across different domains. By understanding how to set up CORS policies on your server, you can ensure secure and efficient cross-domain communication.