Categories
Express Node.js

How to fix those confusing CORS errors when calling your Express API

6 min read

You’ve created an API with Express and you’re busy adding some JavaScript to your front end which will make requests to it. Everything is going great until you load up the front end in your browser and you see a weird error like this in the console:

Access to fetch at 'https://your-api.com/user/1234' from origin 'https://your-website.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Perhaps you’ve then tried setting the request’s mode to no-cors as the error message suggests, but the request to the API still doesn’t work. Even after a bunch of Googling, it’s hard to wrap your head around why this is happening or how to get around it.

The good news is that there’s a library for Express which you can use to help fix these CORS errors, but before we look at fixing them, what do they actually mean? In order to understand these errors, let’s take a look at what CORS is.

What is CORS and why is it ruining your day?

CORS stands for Cross-Origin Resource Sharing, and it’s something which is supported by all modern browsers. It’s a bit of a mouthful, so we’re going to break it down first, and then we can learn about what it actually does.

What is a "resource"?

A resource is the content which is available at a specific URL e.g. an HTML web page, an image, or a JSON API response. It’s effectively the "stuff" which makes up the world wide web.

What is an "origin"?

The origin for a resource is the protocol + domain + port e.g. for the URL https://your-api.com:8080/user/1234 the origin is https://your-api.com:8080. If the URL doesn’t contain a port, then the origin will just be the protocol + domain.

What does Cross-Origin Resource Sharing actually do?

Cross-Origin Resource Sharing is the way in which a web browser ensures that the front end JavaScript of a website (origin A) can only access resources from another origin (origin B) if that origin explicitly allows it to. If it does allow it, then the resource is shared – you guessed it – cross-origin! Phew, we got there in the end.

CORS can help prevent malicious websites from accessing and using data from places that they shouldn’t be. When you see those annoying CORS errors in your browser, it’s actually your web browser doing its best to protect you from what it has identified as a potentially malicious request.

How does CORS work?

The way in which a web browser figures out whether a resource is allowed to be shared cross-origin is by setting an Origin header on requests made by front end JavaScript. The browser then checks for CORS headers set on the resource response. The headers it will check for on the response depend on the type of request which the browser has made, but the response must at least have the Access-Control-Allow-Origin header set to an allowed value for the web browser to make the response available to the front end JavaScript which requested it.

An example CORS request

An example of a cross-origin request would be a GET request made with fetch from the front end JavaScript on your web page – which is hosted on one domain (origin A) – to an API endpoint which you host on a different domain (origin B).

The request made by the browser from the JavaScript on your web page at https://your-website.com/user-profile would contain this information:

> GET /user/1234 HTTP/1.1
> Host: your-api.com
> Origin: https://your-website.com

The Origin request header is automatically set by the web browser – for security reasons you are not able to set its value when you make the request with fetch.

In order for the example CORS request above to work correctly, the response from your API would need to look like this:

< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: https://your-website.com
< Vary: Origin
< Content-Type: application/json; charset=utf-8
< 

{"name":"Existing Person"}

Notice how the value of the Access-Control-Allow-Origin response header matches the value of the Origin response: https://your-website.com. The web browser will see this CORS response header and determine that it has permission to share the response content with the front end JavaScript on your web page.

Now we have a better idea of what CORS is and what it does, it's time to set some CORS headers and fix the errors you're getting on your web page.

How to set CORS headers and get rid of those annoying errors

As you saw in the example above, it's important for the web browser to send the Origin header in the request that it makes to your API, but it's your API which needs to send the all important Access-Control-* headers in the response. These CORS headers are what will tell the web browser whether or not it is allowed to make the response from your API available to your front end JavaScript.

The library you're going to use to help fix the CORS errors you've been battling is the cors middleware package. Head to the directory containing your Express application in your terminal, and let's get it installed:

npm install cors

Note: In this blog post I'm linking to the cors package on GitHub instead of npm as at the time of writing the documentation for this package is out-of-date on on npm.

Once it's installed, you need to require it in your application (directly after you require express is fine):

const cors = require("cors");

If you call the cors middleware in your Express application without passing any configuration options, by default it will add the CORS response header Access-Control-Allow-Origin: * to your API's responses. This means that any origin - i.e. a web page on any domain - can make requests to your API. Unless you're building an API for the general public to use, this is not the behaviour you want, so let's jump right in to configuring the cors middleware so that only your website can make CORS requests to your API:

/**
 * These options will be used to configure the cors middleware to add
 * these headers to the response:
 * 
 * Access-Control-Allow-Origin: https://your-website.com
 * Vary: Origin
 */
const corsOptions = {
	origin: "https://your-website.com"
};

/**
 * This configures all of the following routes to use the cors middleware
 * with the options defined above.
 */
app.use(cors(corsOptions));

app.get("/user/:id", (request, response) => {
	response.json({ name: "Existing Person" });
});

app.get("/country/:id", (request, response) => {
	response.json({ name: "Oceania" });
});

app.get("/address/:id", (request, response) => {
	response.json({ street: "Gresham Lane", city: "Lakeville" });
});

Typically you'll want to enable CORS for all of the routes in your Express application as in the example above, but if you only want to enable CORS for specific routes you can configure the cors middleware like this:

/**
 * These options will be used to configure the cors middleware to add
 * these headers to the response:
 * 
 * Access-Control-Allow-Origin: https://your-website.com
 * Vary: Origin
 */
const corsOptions = {
	origin: "https://your-website.com"
};

// This route is using the cors middleware with the options defined above.
app.get("/user/:id", cors(corsOptions), (request, response) => {
	response.json({ name: "Existing Person" });
});

// This route is using the cors middleware with the options defined above.
app.get("/country/:id", cors(corsOptions), (request, response) => {
	response.json({ name: "Oceania" });
});

/**
 * We never want this API route to be requested from a browser,
 * so we don't configure the route to use the cors middleware.
 */
app.get("/address/:id", (request, response) => {
	response.json({ street: "Gresham Lane", city: "Lakeville" });
});

Enabling "complex" CORS requests

The examples above configure CORS for simple GET requests. For many other types of CORS requests, a CORS "preflight" request will be made by web browsers before the actual CORS request. This preflght request uses the OPTIONS HTTP method and it helps the browser determine whether it will be allowed to make the CORS request.

The cors middleware provides instructions for Enabling CORS Pre-Flight, and allows you to configure the headers that you want to send in the response to a preflight request.

Fear CORS no more

Hopefully this article has helped you understand what CORS is all about, but there will always be times where it's difficult to figure out how you need to configure things for a CORS request to work. Here are some things that have helped me out along the way:

  • Will it CORS? - This fantastic tool will ask you about what you want to do, and then tell you the exact CORS response headers that you need to send for the CORS request to work correctly.
  • CORS HTTP headers - A handy reference which lists all of the CORS headers which you can use.
  • Simple requests and Preflighted requests - The CORS documentation on the Mozilla Developer Network has great explanations of the different types of CORS requests.
  • Path of an XMLHttpRequest(XHR) through CORS - This flowchart on Wikipedia is a helpful visual tool for understanding when a CORS request is considered "complex".
  • Fetch standard: HTTP extensions - This documentation covers the nitty gritty details of how CORS is implemented in the browser Fetch API.

Happy cross-origin resource sharing!