In this post, we will get through what CORS is, why we have it and what is the meaning of CORS.
What is CORS ?
Cross-Origin Resource Sharing (CORS) is an HTTP-header-based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources. CORS also relies on a mechanism by which browsers make a "preflight" request to the server hosting the cross-origin resource, in order to check that the server will permit the actual request. In that preflight, the browser sends headers that indicate the HTTP method and headers that will be used in the actual request.
For example, if we try to fetch some data on example-b.com, and we are trying to fetch it from example-a.com, we would need to add permission on the example-b.com server for example-a.com to be able to get that data because it's disabled by default. We also have two types of requests on frontend:
- Simple
- Preflight
Simple is a type of request that is used when we want to send certain HTTP Methods (GET, HEAD, POST) that don't require an additional request (OPTIONS) to be sent before our request to check if the request is allowed. If we add custom headers to our request then it is not a simple request anymore, now it's a preflight type of request. Here is an image of a simple request:
Animation by Fatima Trtak
Preflight is a type of request that uses additional OPTIONS request that is sent before our request to verify with the server that it supports the HTTP Method that we are trying to send and it's safe to send the request. Some of the methods that are considered preflight are (DELETE, PATCH, PUT, ...). Here is an image of the request with preflight:
Animation by Fatima Trtak
In these two images above you could see a few of the headers that are used when working with CORS: Access-Control-Allow-Origin, Access-Control-Request-Method and Access-Control-Request-Headers. We will talk about them later in this post what they are for.
NOTE: We use CORS only for requests that are not on the same domain because when it's on the same domain we have control over it and there is less chance for security issues. So by default, we don't have CORS on the requests that are on the same domain. For example, if we have our frontend on the localhost:3000 and our backend localhost:5000, every request from frontend to backend will have CORS added, since it's treated as a separate domain. The way we handle that is using something like NGINX, and having an application running on one port 80, and filtering traffic that we get, so we send all the requests to localhost:80 and NGINX does the proxy where frontend goes on frontend port, and backend goes on backend port, and that way we don't have CORS since we host everything on one place and browser doesn't complain about CORS.
How to handle CORS issue
We all encountered the CORS during our programming development and probably just found the solution on the internet and just added it to our project without thinking too much about it as long as it works. But generally, we should understand what are we adding to the project so it doesn't introduce unexpected bugs. CORS is a big part of web development and should be understood by programmers. It's not hard to learn about it, but still, we add options that are not necessary for CORS to work because we just copy the values of the internet. As most of us did, we went on the internet and found a solution similar to this and added it to our code:
1res.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");2res.setHeader(3 "Access-Control-Allow-Methods",4 "GET, POST, OPTIONS, PUT, PATCH, DELETE"5);6res.setHeader("Access-Control-Allow-Headers", "X-Something,content-type");7res.setHeader("Access-Control-Allow-Credentials", true);
This is a usual answer for the CORS issue, where we copy and paste it, even if our server has only GET and POST, we add PUT, PATCH, and DELETE Methods to the server without a reason. Access-Control-Allow-Credentials: true, means that we want to include credentials with the request and that both client that sends the request adding credential: true, and the server that responds with header Access-Control-Allow-Credentials are accepting credentials to be used when making requests. Credentials can be cookies or authorization headers or TLS client certificates. If we add credentials to our request we will notify the browser that we agree to send the credentials with the request and it will add preflight request that will check if server also has it enabled so we can include it in the request. The browser automatically adds cookies from that domain that's making a request in the request, and if the server responds with Access-Control-Allow-Credentials: true, we will get our response in the Javascript code, if not browser will reject the response. Simple GET requests don't have the preflight check, but the browser checks if we sent credentials: true and if the server responded with Access-Control-Allow-Credentials: true then we get the response in our Javascript code, if we don't have these headers then the browser just ignores the response from the server.
And as you can see we send many headers from servers without even needing them, if we don't have credentials with requests than we don't need Access-Control-Allow-Credentials, and if we don't have certain methods we should remove them from Access-Control-Allow-Methods, so our code can contain only what we actually need to work.
Now that we saw an example where we copy the solution from the internet, let's look at how we can solve this without searching for a ready solution, but make a solution ourselves and have an understating of what it does.
The first header that we should look into is Access-Control-Allow-Origin, then Access-Control-Allow-Headers, Access-Control-Allow-Methods, and Access-Control-Allow-Credentials depending if you have authorization on your server.
Access-Control-Allow-Origin
This controls on the server who can actually request and get our data, and if we don't have this header set no one will get it and the browser will throw a CORS error. The first value that we can set and is a preferable option for 98% of cases is the wildcard *. When we set Access-Control-Allow-Origin: *, we tell the browser that anyone can access our data and get the response. Usually, data that we make is globally available so this makes that possible. The next option we have is to set the list of domains that can access our data. For example, if we set Access-Control-Allow-Origin: localhost:3000, this means that only localhost:3000 can access the data on that server and no one else, this is good for companies that want only communication with certain domains for added security protection.
Access-Control-Allow-Headers
Here we add list of headers that our server excepts and supports, for example if we have some authentication "x-key" that we except on our backend we add it in this list so when it's sent from frontend our server can respond that it's ok to send this header. If we have default headers, we can skip this header and browser will handle it.
Access-Control-Allow-Methods
Here we tell the client that sent the request which methods we support, so if they send something we don't support it will throw a CORS error. We should add only the methods we support so it is easy to follow what's going on. Don't add all of them just in case you need them one day.
Access-Control-Allow-Credentials
We already covered this header above but basically, we use this when we want to send credentials to the server. Cookies, authorization headers, or TLS client certificates. We only need it when we are using credentials, in any other case we can leave it out since it's false by default.
These headers are usually the ones we need to enable CORS and make those requests work as planned. In case you have different requirements then you can go and research about it more, best place to start is MDN where you can find almost all the information you need.
Conclusion
Now you should have a better understating of CORS and how to deal with it, we don't need to find and copy a solution because we can make our own. If you find this interesting topic and it helped you share it with others so they can also learn about this topic.