What is CORS
By default, browsers will block certain requests if both the client and the server are not in the same origin. Cross-origin resource sharing (CORS) is a specification designed to allow restricted resources from a remote server in a given origin, to be requested by a client associated to a different origin. An origin, as defined by the RFC6454, implies “identical schemes, hosts and ports”.
Usually the request from the browser will be accompanied by its corresponding HTTP headers, including the request’s origin. Example of the HTTP headers on the request:
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:3001/
Origin: http://localhost:3001
Connection: keep-alive
Cache-Control: max-age=0
If-None-Match: W/”10-iv0euXUvX8F10Ha2yy45d6DFMcI”
How does CORS work?
When CORS is not enabled, the response will not contain the Access-Control-Allow-Origin header and the browser will likely block it, as illustrated by the diagram below.
You will notice that although both the API and the client are in the same domain, the different HTTP ports result in both having different origins.
A Node JS CORS implementation
For this example, we will use a React JS application fetching data from a Node JS API. The principles explained are, however, independent on the technologies used and can be assumed for other scenarios involving cross-origin HTTP communication as well.
index.js (Node JS API)
// API const express = require('express'); const cors = require('cors') const app = express(); const port = 3000; app.get('/cars', (request, response) => { return response.json([ 'Mercedes', 'BMW', 'Toyota', 'Audi' ]); }); app.listen(port, () => { console.log(' > backend started...') });
app.js (React JS front-end)
// front-end import React, { useState, useEffect } from 'react'; import './App.css'; function App() { const [cars, setCars] = useState([]); async function fetchData() { const response = await fetch("http://localhost:3000/cars"); response.json() .then(res => setCars(res)) .catch(err => console.log(err)); } useEffect(() => { fetchData(); },[]); return ( <div className="App"> <ul> {cars.map(car => <li key={car}>{car}</li>)} </ul> </div> ); } export default App;
This API has currently no CORS implementation and therefore, when the client tries to fetch the remote data, the browser will deny and throw the following error in the console:
How can I fix this?
In order for the browser to allow the request, it is necessary for the API to send along the response, the appropriate HTTP header, as mentioned above. For that effect, we will use the CORS Node JS package.
Let’s begin by installing the package:
npm install cors
Then we can simply require it and add the necessary middleware, like so:
index.js with CORS enabled (Node JS API)
const express = require('express'); // Require the CORS package const cors = require('cors'); const app = express(); const port = 3000; // Implement CORS app.use(cors()); app.get('/cars', (request, response) => { return response.json([ 'Mercedes', 'BMW', 'Toyota', 'Audi' ]); }); app.listen(port, () => { console.log(' > backend started...') });
It is of course possible to set different options. According to the documentation, the default options are:
{
“origin”: “*”,
“methods”: “GET,HEAD,PUT,PATCH,POST,DELETE”,
“preflightContinue”: false,
“optionsSuccessStatus”: 204
}
And you can easily modify them by sending an “options” object to cors().
var corsOptions = { origin: 'http://localhost:3333', optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204 } app.use(cors(corsOptions));
After that, the client will be able to successfully accept the data and you will see in the browser console the correct HTTP header.
What if I don’t control the API?
If you do not control the API, the easiest method to bypass the lack of CORS headers might be by the implementation of a proxy that will intercept the headers from the API and will add the missing Access-Control-Allow-Origin header to the transformed response for the client’s consumption.
There is a famous package called CORS-Anywhere deployed in https://cors-anywhere.herokuapp.com, that implements such a proxy. To use it, you can simply append the request URI to the end of its URL and the proxy will transform the headers for you.
app.js (React JS front-end with CORS-anywhere)
// front-end ... async function fetchData() { const response = await fetch("https://cors-anywhere.herokuapp.com/https://example.com/cars"); response.json() .then(res => setCars(res)) .catch(err => console.log(err)); } ...
You can find alternative packages for the proxy, like cors-escape or others. Otherwise, if you are brave enough, you can always have some fun coding your own CORS proxy.