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

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.

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

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.

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

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:

OriginRequest TypeReason
http://localhost:3000/aboutSame-originThe path “/about” is not considered as a part of the origin
http://localhost:3000/shop/product.htmlSame-originThe path “/shop/product.html” is not considered as a part of the origin
http://localhost:5000Cross-originDifferent port (5000 instead of 3000)
https://localhost:3000Cross-originDifferent scheme (HTTPS instead of HTTP)
https://blog.logrocket.comCross-originDifferent 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).

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.

Over 200k developers use LogRocket to create better digital experiencesLearn more →

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.

More great articles from LogRocket:

  • Don't miss a moment with The Replay, a curated newsletter from LogRocket
  • Learn how LogRocket's Galileo cuts through the noise to proactively resolve issues in your app
  • Use React's useEffect to optimize your application's performance
  • Switch between multiple versions of Node
  • Discover how to use the React children prop with TypeScript
  • Explore creating a custom mouse cursor with CSS
  • Advisory boards aren’t just for executives. Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

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.

Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to getan app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, notserver-side

    • npm
    • Script tag
    $ npm i --save logrocket // Code:import LogRocket from 'logrocket'; LogRocket.init('app/id'); 
    // Add to your HTML:<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script><script>window.LogRocket && window.LogRocket.init('app/id');</script> 
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin

Get started now

The ultimate guide to enabling Cross-Origin Resource Sharing (CORS) - LogRocket Blog (2024)
Top Articles
Latest Posts
Article information

Author: Kimberely Baumbach CPA

Last Updated:

Views: 6582

Rating: 4 / 5 (61 voted)

Reviews: 92% of readers found this page helpful

Author information

Name: Kimberely Baumbach CPA

Birthday: 1996-01-14

Address: 8381 Boyce Course, Imeldachester, ND 74681

Phone: +3571286597580

Job: Product Banking Analyst

Hobby: Cosplaying, Inline skating, Amateur radio, Baton twirling, Mountaineering, Flying, Archery

Introduction: My name is Kimberely Baumbach CPA, I am a gorgeous, bright, charming, encouraging, zealous, lively, good person who loves writing and wants to share my knowledge and understanding with you.