Cross Origin Resource Sharing
Getting CORS error in the Developer Console is something that every Frontend Developer has faced when he first started building stuff. Fixing these type of errors won't take you long if you have encountered these issues earlier. I still remember the first time when I had encountered a CORS error, and it took me a quite long just to understand what was wrong with my code. All I had to do was to install a library on my Backend Server to fix this error. Everyone who is reading this article must have heard about CORS, once in his/her life. But do you actually understand the very internals of this mechanism? And the reason behind using CORS? Well, I can assure you one thing that you would be able to explain CORS in the simplest possible way to anyone after reading this article.
Definition of CORS
CORS acronym for Cross Origin Resource Sharing, is basically a way to allow a server to let know a browser(i.e the client, which has a made a request to it for a resource), the domains or URLs(of the web pages) that are allowed to access the server's resources. HTTP headers are used for implementing CORS. Browsers use the fetch and XMLHTTPRequest APIs in order to restrict Cross Origin Requests .
Overview of a Request that demonstrates CORS
Let's suppose that you visit the website https://www.helloWorld.com and after the browser is done loading you have a web page on your screen. The only element present on this web page is a button which when clicked, fetches you the Top 10 most popular songs on Spotify. Now, in order to get this information from Spotify's Database, Spotify has created a Public API, which pulls this data from the Database when you make a POST request to it. So, when you click on the button on the web page, the Javascript code present in the web page, which has been listening to the onclick event for the button, makes a POST request to the Spotify's Public API and you expect to see a list of songs on your screen. But instead there's no change on the web page, it still has only one button, and no matter how many times you click that button you don't get the list of songs. When you open the Developer Console, you see something that looks like the below image
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://localhost:3000/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.
Let https://localhost:300 = Spotify Public API
And this is what a simple CORS error looks like. Now, in order to understand what went wrong, we have look into each request that was sent and the response that was received when we clicked on the button.
HTML code for the Web Page.
<!DOCTYPE html>
<html lang="en">
? <head>
??? <meta charset="UTF-8" />
??? <meta http-equiv="X-UA-Compatible" content="IE=edge" />
??? <meta name="viewport" content="width=device-width, initial-scale=1.0" />
??? <title>Document</title>
? </head>
? <body>
??? <div>
????? <button id="btn">Click Me</button>
??? </div>
??? <script>
????? console.log("Hello");
????? let btn = document.getElementById("btn");
????? btn.addEventListener("click", fun);
????? async function fun() {
??????? const response = await fetch("https://localhost:3000", {
????????? method: "POST",
????????? headers: {
??????????? "Content-Type": "application/json",
????????? },
????????? body: JSON.stringify({ name: "Prashant Pandey" }),
??????? });
??????? console.log(response);
??????? const data = await response.json();
??????? console.log(data);
????? }
??? </script>
? </body>
</html>
You can notice the script tag in the above code. The function fun is called when the button is clicked and then the fetch API is used to make a POST request to the Spotify's Server.
The typical Request, Response flow for our case would be that the browser send's a POST request like the one given below
POST /doc HTTP/1.1
Host: spotify.public.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: https://helloWorld.com/index.html
Content-Length: 55
Origin: https://helloWorld.com
Pragma: no-cache
Cache-Control: no-cache1
And it receives a response like the one given below from the server
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://helloWorld.com
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
You don't have to go in detail about what each line in the above snippet means. The important one's would be explained below.
But this is not how things go around. Let's see what happens.
When the button is clicked, the Web Browser straightaway does not make a POST request to the Spotify's Server. Before making the POST request, it make an OPTIONS request to the API, which would look something like below
OPTIONS /topSongs HTTP/1.1
Host: spotify.public.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://helloWorld.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
The question arises that what is an OPTIONS request, why does the browser makes this extra OPTIONS request, and what does all those fields in the above request mean?
First of all, an OPTIONS request is made by a client when it wants to get some information about the server from the server itself.
领英推荐
Well, what information does the browser want to know about the Spotify's Server in this case?
This is where CORS comes in, as I had mentioned earlier that CORS is a mechanism to let know a browser the domains and URL of the websites or web pages which can fetch resources from it. So, here the Browser wants to ask the Spotify Server if the web page https://helloWorld/topSongs page can fetch the Top 10 Popular Songs from it. And this is why it makes the OPTIONS request before making the POST request.
Now for the header fields in the OPTIONS request. The fields that you would find different from a normal POST or GET request are the Access-Control-Request-Method and the Access-Control-Request-Headers.
The Access-Control-Request-Method is the header which tells the Server the type of request it wants to send to it, in our case a POST request. The Access-Control-Request-Headers tells the server the headers that would be included in the request that it wants to send, in our case the POST request would contain X-PINGOTHER and Content-Type headers.
The request has the Origin and the Host field. Origin gives the URL or domain of the Website which is asking the browser to make the request to the Server(in our case helloWorld.com). The Host field gives the URL of the Server, in our case the Spotify Server(spotify.public.com).
The response that the browser would receive from the Server is given below
HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
The above response has 4 headers that is important to us.
Access-Control-Allow-Origin -> This header mentions the URL or domain of the website which can access the server's resources.
Access-Control-Allow-Methods -> This header mentions the type of Requests that the website can make.
Access-Control-Allow-Headers -> This header mentions the headers which are allowed to be sent with the Requests.
Access-Control-Max-Age -> This header mentions the time for the information provided by this response is valid or the time after which the Browser needs to make an OPTIONS request to the server.
If the response of the OPTIONS request from the server contains the Origin, Methods and the Header that the browser needs to send then and only then the browser moves forward to make the request which it had to make, in our case the POST request to fetch the Top 10 Songs.
As you can see in the response from the server that the domain which is allowed to make request to the server for resources is https://foo.example and not https://helloWorld.com and that is why we were receiving the CORS error in our Developer Console.
So, what would the response for the OPTIONS request look like if Spotify wanted all the websites on the internet to have access to it's resources??
Then the Access-Control-Allow-Origin header would have an asterisk(*) as it's value instead of any particular domain.
HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alivet
So, a Backend Developer can add the Access-Control header to every response to restrict websites from consuming it's data.
Ending Note
In the end CORS proved to be nothing more than a concept of a response header which allows sharing of data between a server and a web page.
Sharing resource between two different origins(Server and Browser).
I hope this article was helpful, do share your views on this in the Comment Section below.
Senior Software Engineer at OnePint.ai | Ex-Nextuple | B.tech at IIIT Bhopal
2 年Very Informative ??
GSDE @ TESCO || Ex-SDE Intern @ Insane AI || Extern @ CommVault || Pratidhi Mentee @ CommVault || IIIT Bhopal'23
2 年Great work!!