How to fix CORS errors in development

Yes, but once and for all

CORS error is one of the most common errors faced during web development and can be a head scratch for newcomers. In this bite, we'll see what it is and how to fix it in our development environment.

The Problem

But what is CORS? How it works and why was firstly introduced?

Well, there are plenty of articles on the web, a good starting point being the dedicated MDN page. What matters the most for the scope of this article is to know that browsers won't directly allow a resource, say a Javascript file, to be make requests to an URL other than the one it was downloaded from.

For example, if a Javascript file downloaded from http://my-oh-my.com tries to perform an HTTP request to http://do-you-wanna-say-goodbye.org (pun intended), the browser will first ask the do-you-wanna-say-goodbye server if it accepts requests from the my-oh-my origin using an OPTIONS, and only after a positive response it will actually perform the request. Otherwise, the browser will block the request and throw a CORS error.

In a development environment, what that triggers the CORS error is usually a backend not configured to accept requests from localhost:<port> origins. Say we are running our web app in localhost:8000 and we spin up the backend from within a Docker container that binds to localhost:9000 . The browser will download the Javascript files from localhost:8000 and soon the application will try to load some data from localhost:9000. This is a CORS request, so the browser will send an HTTP OPTIONS to localhost:9000 . If our backend is not configured to accept CORS requests, our browser will complain.

But why the backend would not be configured properly? There are a number of possible reasons, for example:

  • it is run from a docker image where we don't have the ability to customize HTTP headers

  • it is developed by another team and we don't have control over its source code and configurations

  • it is just not configurable as HTTP headers are hardcoded for any reason

Whatever the reason might be, we must move on with our development and we need to fix this error.

Enter the Matrix Proxy

The easy win is obvious: change the backend configuration to modify CORS headers, so that in our development environment it can accept our web app's CORS requests. But we have seen already that it may not be possible at all.

So what then?

The trick here is to understand that CORS happens only at browser level and have our web server to behave as proxy in between our browser and backend. This way all browser requests will be sent over to it and won't be CORS requests. Our proxy will then be just a pass through, forwarding requests back and forth between frontend and backend. In detail, supposing our backend implements a REST API on localhost:9000/restapi :

  • browser downloads resources, say a Javascript file, from our web server running at localhost:8000

  • web app requests some data reaching localhost:8000/restapi/some-data . No CORS is triggered as the origin is the same

  • given the webserver is acting also as a proxy, it forwards the request localhost:8000/restapi/some-data to localhost:9000/restapi/some-data

  • the response received from backend by the proxy is returned back to browser, that has nothing to complain about security and CORS

Yes, but...in practice?

Let me give you a couple of examples using the most common development web servers: Vite and Webpack Dev Servers.

If we are using Webpack, we can configure our Webpack Dev Server to behave like a proxy in our Webpack configuration file:

// webpack.config.js

module.exports = {
  //...
  devServer: {
    proxy: {
      '/restapi': 'http://localhost:9000'
    },
  },
};

This will map every browser call to localhost:8000/restapi to localhost:9000/restapi as desired. See the official docs for a full reference, there's more that we can do with this configuration.

If instead we are using Vite, things go in a similar fashion:

// vite.config.js

export default defineConfig({
  //...
  server: {
    proxy: {
      '/foo': 'http://localhost:4567',
    },
  },
})

As per Webpack, there is more that we can do with this configuration, see the official documentation for a full reference.

Conclusions

Once understood what CORS is, fixing it is just a matter of a few lines of configuration, also in the worst case, that is when the backend is outside of our control.

To the next bite!

References