What is CORS? Fix Blocked by CORS Policy
Learn everything about cross-origin resource sharing (CORS) and fix the blocked by CORS policy error. Specifically, we will learn about the HTTP Headers (Origin and Access-Control-Allow-Origin) involved with CORS and how to create a CORS proxy.
Table of Contents 📖
- What is CORS?
- What is an Origin?
- CORS and Security
- HTTP Headers and CORS
- What is the Origin Header?
- What is the Access-Control-Allow-Origin Header?
- Origin and Access-Control-Allow-Origin Header Example
- Programming Example
- Programming Example - Client
- Programming Example - Server
- How to Fix a CORS Error?
- Fixing CORS Error - We Control Server
- Fixing CORS Error - We Don't Control the Server
- What is a CORS Proxy and how does it Work?
- Programming a CORS Proxy
- CORS Proxy and Security
- Summary
What is CORS?
Cross-origin resource sharing, or CORS, is a mechanism enforced by the browser that uses origins to determine if a website at one origin can request data from a website at a different origin. But before we go any further, lets define what an origin is.
What is an Origin?
An origin is a combination of the protocol, domain, and port of the URL used to access it. Two entities have the same origin only when the protocol, domain, and port match. For example, the following two URLs have the same origin.
https://wittcode.com:8080
https://wittcode.com:8080
Websites with the same origin are allowed to get data from themselves as they have the same origin. This is a policy enforced by the browser known as the same-origin policy.
These two have different origins because the port is different.
https://wittcode.com:1234
https://wittcode.com:4567
undefined
These two have different origins because the protocol is different (http vs https).
http://wittcode.com
https://wittcode.com
These two have different origins because the domain is different.
https://wittcode.com
https://wittcode-tools.com
CORS and Security
If an origin is requesting data from a different origin, this is known as a cross-origin request. The CORS policy manages cross-origin requests to mitigate their risks. For example, wittcode.com can't just blindly make requests to wittcode-tools.com without passing some security measures. Or even worse, imagine if random sites on the internet could make requests to our bank account? If one origin is making a request to a different origin, such as with an XMLHttpRequest or fetch, it could possibly be restricted/denied by CORS.
HTTP Headers and CORS
So how does CORS determine if a cross-origin request is allowed? The answer is HTTP headers. CORS is an HTTP header based mechanism, meaning it is implemented through the use of HTTP headers. Two important headers for CORS are Origin and Access-Control-Allow-Origin.
What is the Origin Header?
Origin is a request header that indicates the origin that caused the request. For example, if wittcode.com is requesting data from a server, the origin (https://wittcode.com:8080) will be included in the request. The origin header has the following syntax.
Origin: null
Origin: <protocol>://<hostname>
Origin: <protocol>://<hostname>:<port>
Above, protocol is the protocol of the origin (https, http, ftp, etc.), hostname is the domain name or IP address of the origin, and port is the port number of the origin. Origin can also be null in certain cases, such as responses that are network errors, redirects across origins, and a few more.
What is the Access-Control-Allow-Origin Header?
Access-Control-Allow-Origin is a response header indicating whether the response can be shared with the origin requesting the data. In other words, this header determines if a request will be blocked by CORS or not. It has the following syntax.
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: <origin>
Access-Control-Allow-Origin: null
The value * is a wildcard that tells browsers to allow requests from any origin to access the resource. The value
Origin and Access-Control-Allow-Origin Header Example
So, lets say https://wittcode.com:8080 makes a request to https://wittcode-tools.com:8080, the request will contain the Origin header set as Origin: https://wittcode.com:8080. If the request is allowed, the server will respond with the Access-Control-Allow-Origin header set as https://wittcode.com:8080 specifying that this cross-origin request is allowed. If the origin specified on the request does not match the origin specified on the Access-Control-Allow-Origin header on the response, then the request is blocked by the CORS policy.
Programming Example
To better understand CORS, lets create a CORS error ourselves. We will send a request from http://localhost:5050 to http://localhost:1234. This request will be blocked by CORS as the origins are different. Specifically, the port is different (1234 versus 5050). Below is the program we will make. Clicking the "Send Request" button will send the request to localhost:1234. Take note of the CORS error appearing in the console.
Programming Example - Client
To get started, create a file called origin-a.html and place the following boilerplate inside.
<!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>Origin A</title>
</head>
<body>
</body>
</html>
Now, lets create a button to send a cross-origin request. We will give it an onclick attribute that will execute a function that sends a cross-origin request.
<button onclick="sendRequest()">Send Request</button>
Lets create this sendRequest function. We need to create this function inside <!-- origin-a.html -->
<script>
function sendRequest() {
}
</ script>
Now lets set up the request that will be sent to localhost:1234.
const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:1234');
xhr.send();
To do this, we first create an XMLHttpRequest object. Then, we specify that it will be a GET request that should be sent to http://localhost:1234 by using the open() method. Finally, we send the request by using the send() method. Now, when we click on our button, the request will be sent. However, we haven't set up our server to handle this request yet.
Programming Example - Server
To begin, create a file called origin-b.js and import the node http library and the port that this server will listen on. Remember that the port has to be different than the one our client is on as we want this to be a cross-origin request.
const http = require('http');
const port = 1234;
The node http library is a core node module that supports many features of the HTTP protocol. Lets now use it to create an HTTP server.
const server = http.createServer((req, res) => {
});
So, the createServer() method creates a HTTP server. This method takes a function known as a requestListener. This function handles requests from clients as well as the response back to the client. If we receive a request, lets send back the message "hi".
res.write('hi');
res.end();
The write() method sends a chunk of data to the client. The end() method signals to the server that all of the response headers and body have been sent. As such, the server should consider the response complete. Now, lets have our server listen for incoming requests on the provided port.
server.listen(port, () => {
console.log(`Server listening on port ${port}...`);
});
The listen() method makes the HTTP server start listening for connections on the specified port. Now we just need to run our server and client.
node origin-b.js
Now, when we click the button check the browser console and look for the blocked by CORS message.
How to Fix a CORS Error?
A CORS error being fixed relies on the server because the server is responsibble for reporting the allowed origins. If we control the server, we simply need to respond with the proper header. If we don't control the server, it is a little trickier but can be done using a proxy.
Fixing CORS Error - We Control Server
To fix a CORS error on a server we control, we simply need to add the specified origin to the Access-Control-Allow-Origin header. For example, in our programming example above, we just need to send back a header on the response allowing localhost:5050. After we do this, our program will look like the GIF below. Notice how the CORS error is no longer in the console and instead we receive back the string "hi"?
To allow cross origin requests on our server, add the following to origin-b.js.
res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
The setHeader() method sets a single header value on the response that is returned to the client. This tells our server to allow requests from the origin http://127.0.0.1:5500, the url of our client sending the request. Now, lets add some code to our client to handle this response. First, lets create a h1 tag to display the message from the server.
<h1 id="response"></h1>
Now, inside our script tag lets get this element by its id.
const responseH1 = document.getElementById('response');
Now, lets handle the response from the server using our XMLHttpRequest object.
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
responseH1.innerText = xhr.responseText;
}
}
This code sets a listener on our XMLHttpRequest object that is called whenever the readyState property of the request changes. The readyState property is a number 0-4 that represents the status of the XMLHttpRequest. When the readyState property is 4, this means that the request finished and the response is ready to be read. The response is present in the responseText property of the XHR. So, all we need to do is set the innerText of our h1 element to the response text.
Fixing CORS Error - We Don't Control the Server
Fixing a CORS error on a server we don't control is not as simple as setting the response header on the response. Instead, we have to create a CORS proxy.
What is a CORS Proxy and how does it Work?
A CORS proxy is a proxy that allows us to bypass CORS security restrictions with a small change to the request URL. In our previous programming example, our client was sending a request to the target server directly. However, with a CORS proxy, the browser sends the request to a CORS proxy which then forwards the request to the real server. The CORS proxy then returns the response with the "Access-Control-Allow-Origin" header set. The CORS proxy acts as a proxy because it is an intermediary between the client requesting the resource and the server providing the resource.
A CORS proxy works because CORS is enforced by the browser and a CORS proxy is a proxy not a browser. As such, there is no CORS policy blocking it from sending the request to a different origin.
Programming a CORS Proxy
So, lets create a CORS proxy for working with our origin-b server. First, remove the res.setHeader() line from origin-b.js so we get a blocked by CORS again. Then create a file called cors-proxy.js and import the http module.
const http = require('http');
Now, lets create a HTTP server the way we did previously.
const server = http.createServer((proxyReq, proxyRes) => {
});
Note that we named the request and response proxyReq and proxyRes as this application is our proxy. Now, lets use the http module to send a GET request to the origin-b server which is listening on port 1234.
http.get('http://localhost:1234', res => {
});
The http module get() method sends a HTTP GET request to the provided url. The get() method also accepts a callback function that accepts the response from the GET request. Now, lets use some events present on this response object to get the data.
res.on('data', chunk => {
});
res.on('end', () => {
});
res.on('error', err => {
});
The response object present in the get() method callback extends the EventEmitter class. As such, we can use the on() method. The on() method adds a listener to the provided object listening for the provided event. When the event is fired, the callback function (or listener) is called with the provided data. The events we are listening for here are "data", "end", and "error". The "data" event is emitted when a chunk of data is available. The "end" event is emitted when there is no more data to be consumed. The "error" event is emitted when an error occurs. Now, lets respond to each of these events.
let rawData = '';
res.on('data', chunk => {
rawData += chunk;
});
res.on('end', () => {
proxyRes.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
proxyRes.write(rawData);
proxyRes.end();
});
res.on('error', err => {
console.log(err);
});
So, when a chunk of data is retrieved, we want to append it to a variable keeping track of the data. Next, when the "end" event is emitted, we want to set the "Access-Control-Allow-Origin" header on our proxy response so the client doesn't get blocked by CORS. We then write the data back to the client and call the end() method to signal to the server that all of the response headers and body have been sent. Finally, if an error occurs, we just want to log it to the console. Now, lets have our CORS proxy listen on port 1235.
server.listen(1235, () => {
console.log(`Server listening on port 1235...`);
});
We now just need to change the URL in our client request from the origin-b server to the proxy.
xhr.open('GET', 'http://localhost:1235');
The port number for our proxy is 1235 while the port number for the origin-b server is 1234. Now, we just need to run the origin-b server, the cors-proxy, and then run the client.
node origin-b.js
node cors-proxy.js
CORS Proxy and Security
So it is great that we can use a CORS proxy to get around being blocked by CORS. However, CORS proxies should be used carefully. It is recommended to not use other people's CORS proxies as they can read and do anything with the full request and response. So, it is important to only use proxies that are trusted.
Summary
It would be rare for a programmer to never come across a CORS policy issue in their career. If this article was helpful please consider donating so I can pay for the hosting fees and also checkout my Youtube channel WittCode!