Introduction
In this article, we are going to take a look at what CORS is, how you can configure CORS with Express, and how to customize the CORS middleware to your needs.
What is CORS
CORS is shorthand for Cross-Origin Resource Sharing. It is a mechanism to allow or restrict requested resources on a web server depending on where the HTTP request was initiated.
This policy is used to secure a certain web server from access by other websites or domains. For example, only the allowed domains will be able to access hosted files in a server such as a stylesheet, image, or a script.
If you are currently on http://example.com/page1
and you are referring an image from http://image.com/myimage.jpg
you won't be able to fetch that image unless http://image.com
allows cross-origin sharing with http://example.com
.
There is an HTTP header called origin
in each HTTP request. It defines where the domain request has originated from. We can use header information to restrict or allow resources from our web server to protect them.
By default requests from any other origins will be restricted by the browser.
For example, while you are still in the development stage - if you are using a frontend library such as React, your front end application will be served on http://localhost:3000
. Meanwhile, your Express server might be running on a different port such as http://localhost:2020
.
Because of this, you'll need to allow CORS between those servers.
If you see this common error in your browser console. CORS restrictions could be the issue:
CORS is really useful when you're offering a public API and would like to control the access to certain resources and how people use them.
Also, if you want to use your own API or files on a different web page you can simply configure CORS to allow that, while still blocking others out.
Configuring CORS with Express
Let's start off with a fresh project. We'll make a directory for it, enter it and run npm init
with the default settings:
$ mkdir myapp
$ cd myapp
$ npm init -y
Then let's install the required modules. We'll be using express
and the cors
middleware:
$ npm i --save express
$ npm i --save cors
Then let's start creating an express web application with two routes to demonstrate how CORS works.
We'll make a file, called index.js
that acts as a web server, with a couple of request handlers:
const express = require('express');
const cors = require('cors');
const app = express();
app.get('/', (req, res) => {
res.json({
message: 'Hello World'
});
});
app.get('/:name', (req, res) => {
let name = req.params.name;
res.json({
message: `Hello ${name}`
});
});
app.listen(2020, () => {
console.log('server is listening on port 2020');
});
Let's run the app and the server:
$ node index.js
Now, if you go to http://localhost:2020/
- the server should return a JSON message:
{
"message": "Hello World"
}
Alternatively, if you go to http://localhost:2020/something
you should see:
{
"message": "Hello something"
}
Enable All CORS Requests
If you want to enable CORS for all the request you can simply use the cors
middleware before configuring your routes:
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors())
......
This will allow all the routes to be accessed anywhere on the web if that is what you need. So in our example, both routes will be accessible for every domain.
For example, if our server is running on http://www.example.com
and serves content such as images - we allow other domains, such as http://www.differentdomain.com
to refer the content from http://www.example.com
.
Thus, a web page on http://www.differentdomain.com
can use our domain as a source for an image:
<img src="http://www.example.com/img/cat.jpg">
Enable CORS for a Single Route
But if you need a certain route to be accessible and not other routes, you can configure cors
in a certain route as a middleware instead of configuring it to the whole app:
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
app.get('/', cors(), (req, res) => {
res.json({
message: 'Hello World'
});
});
This will allow a certain route to be accessible by any domain. So in your case, only the /
route will be accessible for every domain. The /:name
route will be only accessible for the requests that are initiated in the same domain as the API which is http://localhost:2020
in our case.
For example, if you try to send a fetch request to the /
path from a different origin - it will be successful and you will get the Hello World
message as the response:
fetch('http://localhost:2020/')
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error(err));
You should see the response from the server is successfully logged into the console if you run this code:
{
message: 'Hello World'
}
But if you try to access any other path other than the root path such as http://localhost:2020/name
or http://localhost:2020/img/cat.png
this request will be blocked by the browser:
fetch('http://localhost:2020/name/janith')
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error(err));
You should see the following error if you try to run this code in a different web app:
Configure CORS with Options
You can also use the configuration options with CORS to customize this further. You can use configuration to allow a single domain or subdomain access, configure HTTP methods that are allowed such as GET
and POST
depending on your requirements.
Here is how you can allow a single domain access using CORS options:
var corsOptions = {
origin: 'http://localhost:8080',
optionsSuccessStatus: 200 // For legacy browser support
}
app.use(cors(corsOptions));
If you configure the domain name in the origin - the server will allow CORS from the configured domain. So the API will be accessible from http://localhost:8080
in our case and blocked for other domains.
If we send a GET
request, accessing any path should work, since the options are applied at app-level, not at function-level.
So, if we run the following code and send a request from http://localhost:8080
to http://localhost:2020
:
fetch('http://localhost:2020/')
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error(err));
// Or
fetch('http://localhost:2020/name/janith')
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error(err));
We're allowed to fetch the information from that application and domain.
You can also configure allowed HTTP methods if you'd like:
var corsOptions = {
origin: 'http://localhost:8080',
optionsSuccessStatus: 200 // For legacy browser support
methods: "GET, PUT"
}
app.use(cors(corsOptions));
If we send a POST
request from http://localhost:8080
, it'll be blocked by the browser as only GET
and PUT
are supported:
fetch('http://localhost:2020', {
method: 'POST',
body: JSON.stringify({name: "janith"}),
})
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error(err));
To see a full list of configuration options, please refer the official documentation.
Configuring Dynamic CORS Origins using a Function
If configurations do not satisfy your requirements, you can create your function to customize CORS.
For example, let's assume that you want to allow CORS sharing for .jpg
files http://something.com
and http://example.com
:
const allowlist = ['http://something.com', 'http://example.com'];
const corsOptionsDelegate = (req, callback) => {
let corsOptions;
let isDomainAllowed = whitelist.indexOf(req.header('Origin')) !== -1;
let isExtensionAllowed = req.path.endsWith('.jpg');
if (isDomainAllowed && isExtensionAllowed) {
// Enable CORS for this request
corsOptions = { origin: true }
} else {
// Disable CORS for this request
corsOptions = { origin: false }
}
callback(null, corsOptions)
}
app.use(cors(corsOptionsDelegate));
The callback function will accept two parameters. The first one is an error where we passed null
and the second one is options where we passed { origin: false }
. The second parameter could be many options that are constructed using the request
object from the Express request handler.
So a web app which is hosted on http://something.com
or http://example.com
would be able to refer to an image with .jpg
extension from the server as we have configured in our custom function.
So the following image attachment will be successful from either of these:
<img src="http://yourdomain.com/img/cat.jpg">
But the following attachment will be blocked:
<img src="http://yourdomain.com/img/cat.png">
Loading List of Allowed Origins from as Data Source
You can use also use a list of allowed domains from a database or using any backing data source to allow CORS:
var corsOptions = {
origin: function (origin, callback) {
// Loading a list of allowed origins from the database
// Ex.. origins = ['http://example.com', 'http//something.com']
database.loadOrigins((error, origins) => {
callback(error, origins);
});
}
}
app.use(cors(corsOptions));
Conclusion
In this article, we've covered what CORS is and how you can configure it with Express. Then, we've set up CORS for all requests, for specific requests, added options and restrictions as well as defined a custom function for dynamic CORS configuration.