What is CORS ? Why does it happen ? How to solve for it ?
Understanding how Cross-Origin Resource sharing and the Single Origin Policy work.
If you have ever built a web based project, then I am sure you have come across the very annoying “CORS error” at some point. In this post I will try to break it down, and explain why it is actually quite useful.
CORS stands for Cross-Origin Resource Sharing.
Lets break it down:
Cross-Origin: Origin is basically the domain name from which the request is being called from. Two URLs are said to have the same origin if the protocol, port and host are same for both. Cross origin simply means a different origin.
Resource: Resource is simply whatever is being requested for by the request. It could typically be an API endpoint or media items.
Now Cross-Origin Resource Sharing is a rule that restricts/allows resources requested from a certain origin to be shared with a different origin. For example, say there is an origin https://www.abc.com, then CORS can prevent any other domain like https://www.pqr.com from accessing the resources on https://www.abc.com. This check is done by your browser and the error is also thrown by your browser.
CORS actually exists because of another policy known as the Same Origin Policy. Let’s see what that is.
Same Origin Policy (SOP)
SOP is a security policy on the browser that restricts how a document or script that is loaded from one origin can interact with a resource or data from another origin.
SOP allows an origin to send requests to other origins but does not allow access to the response.
This is very important for modern web applications that depend on cookies to maintain authenticated user information like access tokens, session information, state information etc.. Web servers use this information in cookies to verify the user and share sensitive information with them or take actions on behalf of the user.
Now imagine you were on your bank’s website, and it stores your access token in the browser cookie. The bank server uses this token to identify you and shares sensitive information like account balance, statements, or even perform actions like transferring money. Now say you visit another website, and this one has a malicious script that takes the bank’s access token from your cookie and makes a request to your bank to transfer some money to an account. Your banks checks the access token, identifies its you and happily makes the transaction.
This is the reason why SOP exists, and we do not want scripts from an origin to access resources on other origins.
Also note that SOP mainly applies only to scripts. Resources such as images, CSS and dynamically loaded scripts can be accessed across origins via the corresponding HTML tags. SOP also applies to fonts and iframes.
So now what do you do when you want to access some resource from another origin ? It could be a public API endpoint, or maybe a font.
Back to CORS
CORS is basically a technique for relaxing the Same Origin Policy.
CORS allows servers to use a header — ‘Access-Control-Allow-Origin’, for specifying origins that can access its resources. These are the trusted origins already known by the server. It also supports the wildcard entry ‘*’ to allow any origin to request files.
For example, say you are on https://www.abc.com. When you make a GET request to a server for any resource, your browser also includes a header called Origin.
origin: https://www.abc.com
Now the server receives this request, and responds with the requested data along with the Access-Control-Allow-Origin header:
access-control-allow-origin: https://www.abc.com
It could also be ‘*’ in case of a publicly accessible API. It could also be a comma separated list of origins.
If the Access-Control-Allow-Origin value does not match with the Origin, then your browser throws the CORS error.
One thing to note here is that, it is your browser that checks if the Access-Control-Allow-Origin header included in the response and the origin in the request are the same or not. Only if they are the same, the browser permits access to the response data.
Preflight Request
A CORS preflight is a request with method OPTIONS, which is made before the actual request to check if the CORS protocol is understood and the server is aware about using specific methods and headers.
Some requests that do not trigger a preflight are called Simple Requests. They are GET, HEAD and POST. But for the case of other request methods and cases where the headers of GET, HEAD and POST are modified, the browser automatically issues a preflight request.
For example, if you need to make a DELETE request, the browser automatically issues a preflight request with OPTIONS method and the following headers to the server:
origin: https://www.abc.com
access-control-request-method: DELETE
access-control-request-headers: origin, authorization
Here we are requesting for the DELETE method through Access-Control-Request-Method header. Access-Control-Request-Headers are a list of headers that we will send with the actual DELETE request. Any custom headers need to be included here.
The server response to this OPTIONS request contains the following:
access-control-allow-methods: POST, GET, OPTIONS, DELETE
access-control-allow-origin: https://www.abc.com
access-control-allow-headers: origin, authorization, cache-control
Here Access-Control-Allow-Methods is a list of methods that the server accepts. Access-Control-Allow-Origin is the permitted origin. Access-Control-Allow-Headers is a list of headers that the server permits. If any of these does not match against the actual parameters of the request, the request is not made at all.
Solutions to the CORS Error
There are two main ways to solve for the CORS error.
The best way is to add CORS support to your web server. Usually you simply need to install a package, so it depends on the framework you are using to build your backend. This package takes care of the options request and also allows you to set the values of allow headers.
Another common way to bypass this problem is to use a proxy. For example, imagine your frontend is served on https://www.abc.com and your backend server is running on https://www.abc.com:8000. Now if you make a request to your backend server it throws a CORS error because the port is different. Now you can configure your proxy to say that all requests coming on https://www.abc.com/api should be routed to port 8000. In this way, when the browser checks the origin is the same https://www.abc.com.
Another way would be to use some plugins that turn off the CORS check on your browser. But it should be obvious by now why this is not such a great idea.
References:
Same-origin policy — Web security | MDN
CORS errors — HTTP | MDN
What is ‘CORS’? What is it used for?
What is CORS (cross-origin resource sharing)?
Cross-origin resource sharing — Wikipedia
CORS and the Access-Control-Allow-Origin response header
Preflight request — MDN Web Docs Glossary: Definitions of Web-related terms
Cross-Origin Resource Sharing (CORS) — HTTP | MDN