In October, I wrote about cross-origin resource sharing (CORS). I explained that if you want a resource on your server to be accessible from other origins, you need to set the
I offered some examples using a simple HTTP server in Node.js. Because it's more common to use Express, I thought I'd explain how to write a simple CORS middleware function.Access-Control-Allow-Origin
HTTP response header.
The cors
package
The cors
package is an official middleware function, much like the express.json()
method. The only difference is that the former isn't part of the core Express framework, so you have to install it via npm. With that in mind, I'm explaining how to write your own CORS middleware for two reasons:
- It's a learning experience.
- You probably don't need the
cors
package if you're only handling simple requests. You don't want to be one of those developers whoinstalls npm packages like they're popping painkillers
.
If you need robust CORS middleware, you should probably just use the cors
package. It handles all aspects of CORS including preflight requests.
A simple Express server
The following is a simple Express server with a single GET
handler for the root route (/
). It sends a simple JSON response with .code
and .message
properties. We'll add a CORS middleware function to this server.
const http = require("http");
const express = require("express");
let app = express();
app.set("json spaces", 2);
app.get("/", (_req, res) => {
let code = 200;
res.send({ code, message: http.STATUS_CODES[code] });
});
let port = 8080;
app.listen(port, () => {
console.log(`Listening on port ${port}`);
});
Allowing any origin
Let's declare a middleware function, cors()
, with three parameters:
_req
- TheRequest
object.res
- TheResponse
object.next
- The next middleware function in the stack.
When Express calls the cors()
middleware function, it will pass in arguments for the _req
, res
, and next
parameters in that order. Because we won't be reading the value of the _req
parameter, we've prefixed it with an underscore. This is merely a convention that tells well-meaning developers: I know I'm not reading the value of this parameter, but the parameter needs to be in this position, so please don't remove it.
function cors(_req, res, next) {}
As I wrote in my previous post, To allow CORS requests from any origin, you can use the
To set the header, let's invoke the *
wildcard as the value of the Access-Control-Allow-Origin
header.res.set()
method, passing in strings for the header name and value. Then we'll invoke next()
, i.e. the next middleware function in the stack.
function cors(_req, res, next) {
res.set("Access-Control-Allow-Origin", "*");
next();
}
Allowing a single origin
In my previous post, I wrote To allow CORS requests from a single origin, you can use the origin as the value of the
To handle this, let's update the call to the Access-Control-Allow-Origin
header. For example, to allow CORS requests from my website only, you would use https://barker.codes
as the value.res.set()
method.
function cors(_req, res, next) {
res.set("Access-Control-Allow-Origin", "https://barker.codes");
next();
}
In my previous post, I also quoted the MDN Web Docs on the significance of the Vary
header in this situation:
Suppose the server sends a response with an
Access-Control-Allow-Origin
value with an explicit origin (rather than the "*
" wildcard). In that case, the response should also include aVary
response header with the valueOrigin
— to indicate to browsers that server responses can differ based on the value of theOrigin
request header.
Let's update the call to the res.set()
method again. Instead of passing in strings for the header name and value, we'll pass in an object literal. This lets us set the Access-Control-Allow-Origin
and Vary
headers at the same time.
function cors(_req, res, next) {
res.set({
"Access-Control-Allow-Origin": "https://barker.codes",
Vary: "Origin",
});
next();
}
Allowing a list of origins
In my previous post, I went on to quote the MDN Web Docs again, this time about allowing a list of origins:
Limiting the possible
Access-Control-Allow-Origin
values to a set of allowed origins requires code on the server side to check the value of theOrigin
request header, compare that to a list of allowed origins, and then if theOrigin
value is in the list, set theAccess-Control-Allow-Origin
value to the same value as theOrigin
value.
Let's update our code to handle this. Just before the middleware function, we'll declare an ALLOWED_ORIGINS
constant with a Set
of allowed origins. Declaring the constant outside the middleware function is more performant because it prevents it from being declared every time the function is invoked.
In the body of the middleware function, we'll get the value of the Origin
header by invoking the req.get()
method and assigning the return value to the origin
variable. If the set of allowed origins .has()
the value of the origin
variable, we'll set the Access-Control-Allow-Origin
header to the value. Then we'll set the Vary
header and invoke next()
as before.
const ALLOWED_ORIGINS = new Set([
"https://barker.codes",
"https://gomakethings.com",
]);
function cors(_req, res, next) {
let origin = req.get("Origin");
if (ALLOWED_ORIGINS.has(origin)) {
res.set("Access-Control-Allow-Origin", origin);
}
res.set("Vary", "Origin");
next();
}
Using the middleware function
To use the middleware function for every route, you can pass it into the app.use()
method.
app.use(cors);
app.get("/", (_req, res) => {
res.send({ code: 200, message: http.STATUS_CODES[200] });
});
To use the middleware function for a single route, you can pass it into appropriate HTTP request method, e.g. app.get()
or app.post()
. You should place it after the path but before your route handler. In this example, the route handler is the next middleware function in the stack, i.e. the value of the next
parameter in the middleware function!
app.get("/", cors, (_req, res) => {
res.send({ code: 200, message: http.STATUS_CODES[200] });
});
Summary
In today's post, you learned how to write a simple CORS middleware function in Express. If you're only handling simple requests, this could be a better option than installing yet another dependency. If you're using more complex features of CORS, such as handling requests that need to be preflighted, it would probably be worth installing the official cors
package.