Nitin Ranganath I'm a computer engineering student and an avid full-stack developer who loves to build for the web and mobile. I create user-centric websites with React, TypeScript, Node.js, and other JavaScript technologies.

The ultimate guide to enabling Cross-Origin Resource Sharing (CORS)

6 min read 1774

Guide to Cross-origin-sharing CORS

Consider the following situation: you’re trying to fetch some data from an API on your website using fetch() but end up with an error.

You open up the console and see either “No Access-Control-Allow-Origin header is present on the requested resource,” or “The Access-Control-Allow-Origin header has a value <some_url> that is not equal to the supplied origin” written in red text, indicating that your request was blocked by CORS policy.

Blocked Request due to CORS

Seem familiar? With over 10,000 questions posted under the cors tag on StackOverflow, it is one of the most common issues that plague frontend developers and backend developers alike. So, what exactly is the CORS policy and why do we face this error often?

What is Cross-Origin Resource Sharing (CORS)?

Interestingly, this is not an error as we portray it, but rather the expected behavior. Our web browsers enforce the same-origin policy, which restricts resource sharing across different origins. Cross-origin resource sharing, or CORS, is the mechanism through which we can overcome this barrier. To understand CORS, let us first understand the same-origin policy and its need.

The same-origin policy

In simple terms, the same-origin policy is the web version of “don’t talk to strangers” incorporated by the browser.

All modern web browsers available today follow the same-origin policy that restricts how XMLHttpRequest and fetch requests from one origin interact with a resource from another origin. What’s an origin, exactly?

It’s the combination of a scheme, domain, and port. The scheme could be HTTP, HTTPS, FTP, or anything else. Similarly, the port can also be any valid port number. Same-origin requests are essentially those requests whose scheme, domain, and port match. Let’s look at the following example.

Assuming our origin is http://localhost:3000, the requests can be categorized into same-origin or cross-origin requests as follows:

Origin Request Type Reason
http://localhost:3000/about Same-origin The path “/about” is not considered as a part of the origin
http://localhost:3000/shop/product.html Same-origin The path “/shop/product.html” is not considered as a part of the origin
http://localhost:5000 Cross-origin Different port (5000 instead of 3000)
https://localhost:3000 Cross-origin Different scheme (HTTPS instead of HTTP)
https://blog.logrocket.com Cross-origin Different scheme, domain, and port

This is the reason why your frontend running on http://localhost:3000 cannot make API calls to your server running http://localhost:5000 or any other port when you develop single-page applications (SPAs).

We made a custom demo for .
No really. Click here to check it out.

Also, requests from origin https://mywebsite.com to origin https://api.mywebsite.com are still considered cross-site requests even though the second origin is a subdomain.

Due to the same-origin policy, the browser will automatically prevent responses from cross-origin requests from being shared with the client. This is great for security reasons! But not all websites are malicious and there are multiple scenarios in which you might need to fetch data from different origins, especially in the modern age of microservice architecture where different applications are hosted on different origins.

This is a great segue for us to deep dive into CORS and learn how to use it in order to allow cross-origin requests.

Allowing cross-site requests with CORS

We’ve established that the browser doesn’t allow resource sharing between different origins, yet there are countless examples where we are able to do so. How? This is where CORS comes into the picture.

CORS is an HTTP header-based protocol that enables resource sharing between different origins. Alongside the HTTP headers, CORS also relies on the browser’s preflight-flight request using the OPTIONS method for non-simple requests. More on simple and preflight requests later in this article.

Because HTTP headers are the crux of the CORS mechanism, let’s look at these headers and what each of them signifies.

Access-Control-Allow-Origin

The Access-Control-Allow-Origin response header is perhaps the most important HTTP header set by the CORS mechanism. The value of this header consists of origins that are allowed to access the resources. If this header is not present in the response headers, it means that CORS has not been set up on the server.

If this header is present, its value is checked against the Origin header of request headers. If the values match, the request will be completed successfully and resources will be shared. Upon mismatch, the browser will respond with a CORS error.

To allow all origins to access the resources in the case of a public API, the Access-Control-Allow-Origin header can be set to * on the server. In order to restrict only particular origins to access the resources, the header can be set to the complete domain of the client origin such as https://mywebsite.com.

Access-Control-Allow-Methods

The Access-Control-Allow-Methods response header is used to specify the allowed HTTP method or a list of HTTP methods such as GET, POST, and PUT that the server can respond to.

This header is present in the response to pre-flighted requests. If the HTTP method of your request is not present in this list of allowed methods, it will result in a CORS error. This is highly useful when you want to restrict users from modifying the data through POST, PUT, PATCH, or DELETE requests.

Access-Control-Allow-Headers

The Access-Control-Allow-Headers response header indicates the list of allowed HTTP headers that your request can have. To support custom headers such as x-auth-token, you can set up CORS on your server accordingly.

Requests that consist of other headers apart from the allowed headers will result in a CORS error. Similar to the Access-Control-Allow-Methods header, this header is used in response to pre-flighted requests.

Access-Control-Max-Age

Pre-flighted requests require the browser to first make a request to the server using the OPTIONS HTTP method. Only after this can the main request be made if it is deemed safe. However, making the OPTIONS call for each pre-flighted request can be expensive.

To prevent this, the server can respond with the Access-Control-Max-Age header, allowing the browser to cache the result of pre-flighted requests for a certain amount of time. The value of this header is the amount of time in terms of delta seconds.

Overall, here’s the syntax of how CORS response headers look like:

Access-Control-Allow-Origin: <allowed_origin> | *
Access-Control-Allow-Methods: <method> | [<method>]
Access-Control-Allow-Headers: <header> | [<header>]
Access-Control-Max-Age: <delta-seconds>

Simple requests vs. pre-flighted requests

Requests that do not trigger a CORS preflight fall under the category of simple requests. However, the request has to satisfy some conditions only after it is deemed as a simple request. These conditions are:

  1. The HTTP method of the request should be one of these: GET, POST, or HEAD
  2. The request headers should only consist of CORS safe-listed headers such as Accept, Accept-Language, Content-Language, and Content-Type apart from the headers automatically set by the user agent
  3. The Content-Type header should have only either of these three values: application/x-www-form-urlencoded, multipart/form-data, or text/plain
  4. No event listeners are registered on the object returned by the XMLHttpRequest.upload property if using XMLHttpRequest
  5. No ReadableStream object should be used in the request

On failing to satisfy either of these conditions, the request is considered to be a pre-flighted request. For such requests, the browser has to first send a request using the OPTIONS method to the different origin.

This is used to check if the actual request is safe to send to the server. The approval or rejection of the actual request depends on the response headers to the pre-flighted request. If there is a mismatch between these response headers and the main request’s headers, the request is not made.

Enabling CORS

Let’s consider our initial situation where we faced the CORS error. There are multiple ways we could resolve this issue depending on whether we have access to the server on which the resources are hosted. We can narrow it down to two situations:

  1. You have access to the backend or know the backend developer
  2. You can manage only the frontend and cannot access the backend server

If you have access to the backend:

Because CORS is just an HTTP header-based mechanism, you can configure the server to respond with appropriate headers in order to enable resource sharing across different origins. Have a look at the CORS headers we discussed above and set the headers accordingly.

For Node.js + Express.js developers, you can install the cors middleware from npm. Here is a snippet that uses the Express web framework, along with the CORS middleware:

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

app.use(cors());

app.get('/', (req, res) => {
  res.send('API running with CORS enabled');
});

app.listen(5000, console.log('Server running on port 5000'));

If you don’t pass an object consisting of CORS configuration, the default configuration will be used, which is equivalent to:

{
  "origin": "*",
  "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
  "preflightContinue": false,
  "optionsSuccessStatus": 204
}

Here is how you could configure CORS on your server which will only allow GET requests from https://yourwebsite.com with headers Content-Type and Authorization with a 10 minutes preflight cache time:

app.use(cors({
  origin: 'https://yourwebsite.com',
  methods: ['GET'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  maxAge: 600
}));

While this code is specific to Express.js and Node.js, the concept remains the same. Using the programming language and framework of your choice, you can manually set the CORS headers with your responses or create a custom middleware for the same.

If you only have access to the frontend:

Quite often, we may not have access to the backend server. For example, a public API. Due to this, we cannot add headers to the response we receive. However, we could use a proxy server that will add the CORS headers to the proxied request.

The cors-anywhere project is a Node.js reverse proxy that can allow us to do the same. The proxy server is available on https://cors-anywhere.herokuapp.com/, but you can build your own proxy server by cloning the repository and deploying it on a free platform like Heroku or any other desired platform.

In this method, instead of directly making the request to the server like this:

fetch('https://jsonplaceholder.typicode.com/posts');

Simply append the proxy server’s URL to the start of the API’s URL, like so:

fetch('https://cors-anywhere.herokuapp.com/https://jsonplaceholder.typicode.com/posts');

Conclusion

As we learn to appreciate the same-origin policy for its security against cross-site forgery attacks, CORS does seem to make a lot of sense. While the occurrences of the red CORS error messages in the console aren’t going to magically disappear, you are now equipped with the knowledge to tackle these messages irrespective of whether you work on the frontend or the backend.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Nitin Ranganath I'm a computer engineering student and an avid full-stack developer who loves to build for the web and mobile. I create user-centric websites with React, TypeScript, Node.js, and other JavaScript technologies.

One Reply to “The ultimate guide to enabling Cross-Origin Resource Sharing (CORS)”

Leave a Reply