- Published on
How to Enable CORS in Node.js Without Express CORS Middleware
Introduction
It's very easy to simply install the cors middleware to handle all the CORS
stuff while using Node.js as your backend. This, however, leaves you with a very superficial understanding of how the Cross-Origin Resource Sharing
mechanism works.
In this article, we will add CORS
to a very simple http server
in Node.js without using Express or any other middleware.
Table of Contents
Create a Node.js HTTP Server
As always, you first need to init a node app inside your project folder.
$ mkdir learn-cors
$ cd learn-cors
$ npm init --yes
$ touch index.js
This will create a package.json file inside the learn-cors
directory on your PC.
Now open the folder in the text editor of your choice. I use Visual Studio Code.
$ code .
The package.json
will look something like this by default.
{
"name": "learn-cors",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
You can see that this is simply a regular JSON file that stores some information about your project. You don't really need to do much here except change the scripts field.
In scripts, delete the test
field and replace it with serve
. It should look like this after the change.
{
...
"scripts": {
"serve": "node index.js"
}
}
This will allow you to simply run your server using the npm run serve command in the terminal.
Now, open the index.js
file where the meat of our code will live. Put the following code in there.
const http = require('http')
const port = 8080
http
.createServer((req, res) => {
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'OPTIONS, POST, GET',
'Access-Control-Max-Age': 2592000, // 30 days
/** add other headers as per requirement */
}
if (req.method === 'OPTIONS') {
res.writeHead(204, headers)
res.end()
return
}
if (['GET', 'POST'].indexOf(req.method) > -1) {
res.writeHead(200, headers)
res.end('Hello World')
return
}
res.writeHead(405, headers)
res.end(`${req.method} is not allowed for the request.`)
})
.listen(port)
Adding Headers To The Response
If you look through the flow, you'll see that I'm simply creating an HTTP server which will handle the incoming request to the server.
To keep things simple, we will not handle routes and other method types other than GET
and POST
because that is out of the scope of this article.
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'OPTIONS, POST, GET',
'Access-Control-Max-Age': 2592000, // 30 days
/** add other headers too */
}
The headers variable will store a Javascript Object containing the standard HTTP Access-Control-*
. As I mentioned in the comment too, you can always add other things to the header too. To make CORS
work correctly, you need at least these three which I have added to the headers variable.
The Access-Control-Allow-Origin
field tells the browser about the domains from where the requests should be accepted. The * denotes that any domain is allowed. You'll probably want to set it to your website URL.
The Access-Control-Allow-Methods
field tells the browser which request method this server supports. You can customize this one to your liking.
The Access-Control-Max-Age
header is the number of seconds for which the pre-flight request's response is to be stored in the browser cache.
You can now try to run the server as follows.
$ npm run serve
You can hit the endpoint using curl
as follows:
curl http://localhost:8080
The response will be:
Hello World
This request worked perfectly and didn't have any CORS
error. This is because the request to the http://localhost:8080
was a simple
GET request. In the simple
requests, you are only allowed to use the GET
, POST
, and HEAD
HTTP methods.
Another limitation of a simple request is that they don't allow you to send any forbidden headers in the request. One among the forbidden (unsafe) headers is the Authorization
header.
If you have ever dealt with making APIs, you know that the Authorization
header is very useful for user validation and authorization. But, with these simple requests, you can't make a request to the server which has the authorization header in the request from the client.
You'll get the following error in the browser console if you add the Authorization
header to your request:
Failed to load http://localhost:8080 Response to preflight request didn't pass access control check: No 'Access-Control-Allow-Origin' header is present in the requested resource. Origin http://localhost:8080 is therefore not allowed to access. The response had HTTP status code 405. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Preflight Requests
Along with the simple requests, we also have preflight requests. I
Whenever you send a non-simple request, the browser sends a preflight request to the server. This request checks the server with the OPTIONS
request method to see what headers are allowed in the request.
Based on the response to this OPTIONS
request, the browser decides whether the request is safe to send.
The conditions for a preflight request are as follows:
The request method is among the following:
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
OR, if your request to the server has one or more of these headers:
Accept-Charset
Accept-Encoding
Access-Control-Request-Headers
Access-Control-Request-Method
Connection
Content-Length
Cookie
Cookie2
Date
DNT
Expect
Host
Keep-Alive
Origin
Referer
TE
Trailer
Transfer-Encoding
Upgrade
Via
OR, if the Content-Type
in the request header is anything other than:
application/x-www-form-urlencoded
multipart/form-data
text/plain
To handle all this, you can simply add a check for the request method in your server.
In the index.js, the if(/** clause */)
which checks if the req.method
is OPTIONS
handles this. It simply writes the headers to the response and results in a 204 (No Content)
to the browser.
if (req.method === 'OPTIONS') {
res.writeHead(204, headers)
res.end()
return
}
When the browser sends a preflight request to your server, you can set the headers in the OPTIONS
request and end the response with 204
code. From now, whenever the browser sends the request, your server will handle it perfectly.