Stop Cursing CORS

Leire Polo
TribalScale
Published in
6 min readJun 8, 2020

Most people doing frontend development at some point deal with CORS issues. The problem is that most people don’t know what is happening, or rather, why certain things are happening. They just try different solutions listed on Stackoverflow.

So throughout this blog, I’m going to explain CORS, the famous “CORS issues” and how to deal with them.

Know your enemy. What is CORS?

Evil, just pure evil 👹

CORS = Cross-origin resource sharing. CORS is the mechanism that allows your website to request resources to a different domain where it’s being hosted.

Why does it exist?🤔

To protect us! It’s our friend!❤️🐕

Most of the time a website will require resources hosted in the same place as the website is, for example, making API calls to the same backend that is serving the website.🏡

So this policy would be the first layer of protection to avoid other unknown people using our API.⚔️

Why do we end up having CORS issues? 😡

Because in some scenarios we need to request resources from one origin to a different one. For example, when our API is hosted in a different domain than our website (any 3rd party API), or when you need to use web-fonts.

CORS doesn’t allow thisby default, so an error will appear that can be seen in the browser console.

What is happening between our browser and the destination of our requests?

Web browsers can use special headers to determine whether or not an XMLHttpRequest call should continue or fail.

Let’s get into more technical detail… 👇👇👇👇👇👇👇

OPTIONS request 🧐

Most people know the basic request methods (POST, GET, PUT) but not everyone is familiarized with the OPTIONS method. This request method is used in order to ask for information to a specific endpoint about what communication options it supports.

This request is used to determine the exact CORS capabilities of the server, which is in turn used to determine whether or not the intended CORS protocol is understood. If the result of the OPTIONS call dictates that the request cannot be made, the actual request to the server will not be executed.

The options contained in an OPTIONS request are the following:

  • Access-Control-Request-Method: The intended method of the request (e.g., GET or POST)
  • Access-Control-Request-Headers: An indication of the custom headers that will be sent with the request
  • Origin: The usual origin header that contains the script's current origin

So our client is asking🤔:

“I would like to make a GET request with the Content-Type and Accept headers from http://localhost:3000 — is that possible?”.

curl -i -X OPTIONS localhost:3001/api/ping \\ -H 'Access-Control-Request-Method: GET' \\ -H 'Access-Control-Request-Headers: Content-Type, Accept' \\ -H 'Origin: <http://localhost:3000>'

The server will include some Access-Control-* headers within the response to indicate whether the request that follows will be allowed or not. These include:

  • Access-Control-Allow-Origin: The origin that is allowed to make the request, or * if a request can be made from any origin
  • Access-Control-Allow-Methods: A comma-separated list of HTTP methods that are allowed
  • Access-Control-Allow-Headers: A comma-separated list of the custom headers that are allowed to be sent
  • Access-Control-Max-Age: The maximum duration that the response to the preflight request can be cached before another call is made

The response would then be examined by the browser to decide whether to continue with the requestor to abandon it.

So our server is replying🤓:

“I accept requests from every domain (*) and I allow the following methods GET, HEAD, PUT, PATCH, POST, DELETE”

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
Vary: Access-Control-Request-Headers
Access-Control-Allow-Headers: Content-Type, Accept
Content-Length: 0
Date: Fri, 05 Apr 2019 11:41:08 GMT
Connection: keep-alive

So if the first request the client is trying to perform meets the requirements it sends the initial request and the process continues normally, and everyone is happy 🎊🎊🎉😁 . If it doesn’t you get a dramatic CORS error which means your initial request wasn’t even sent and you cry 😭 . This is usually the process to follow:

So, how do we fix it? 🔧

If a CORS issue appears during development, because we are using a local development server, this is not an “error”, it is just an inconvenience that we will learn to solve.

🤔Maybe you are thinking about changing the “origin” header to avoid CORS, forget about it, ‘origin’ is a forbidden header which means you can’t update it.🙄

🤔The next and most common solution is to ask the server to set the headers to *** (allow all)**.

💩🤦‍♀️This is not a good practice, the final domain for the frontend should be whitelisted of course, but to simply allow all is a breach of security that shouldn’t be encouraged.🤦‍♂️💩

While developing our domain is usually localhost and toward the outside world that domain is our IP, unless you whitelist it, which can be ineffective if the IP changes, it is never allowed by CORS restrictions unless you allow all domains. 🤷‍♂️🤷‍♀️

🤓Now the most efficient and least painful way to bypass this restriction for development is to use a proxy. This way we do the request to a proxy server that allows our requests and this one does the request to the original API domain. This request will be performed by a proxy server, not a browser, so it will be able to perform the requests without restrictions. It will also send the original content back to our frontend in the response from the API.👌

There are already built proxy servers, so you don’t have to create your own, just install one cors-proxy from npm and you are all set.

Setting up a CORS proxy 🛠

In my case, I used this:

https://www.npmjs.com/package/local-cors-proxy but there are hundreds. You can install it locally in your project or globally on your computer, depending on if you want to add it to your project development dependencies and make it run while you are developing.

(Usually automating stuff is a good idea, just saying🙄)

In this case, the steps are easy to follow, but just in case I’m going to review them one by one:

  • Install dependency by running the following command:
npm i local-cors-proxy
  • Start proxy for the specific domain if it’s giving you issues:
lcp --proxyUrl <https://www.yourdomain.ie>
  • In your client update your API endpoint to point it to your proxy:

From this:

<https://www.yourdomain.ie/movies/list>

To this:

<http://localhost:8010/proxy/movies/list>

As I hope you can see now, there is not magic surrounding CORS🧙‍♂️, it’s not something “the backend is supposed to fix”, and hopefully you will be able to give the right response to one question I hear often: “Why does it work when I make the request using curl in the console but it doesn’t when I use the browser?”

Leire is an Engineer Manager working at TribalScale. She enjoys learning and teaching everything Javascript related. When she is not coding, reading, or teaching articles she is playing video games or practising sports.

--

--