CORS error on request, with headers and webpack-dev-server proxy configured

11,367

Solution 1

After all this, I had no choice but to update the backend with a couple of headers when receiving a preflighted (OPTIONS) request:

  • Access-Control-Allow-Headers, which contains whatever the request has on that same header;
  • Access-Control-Allow-Origin, which is set to *. Since in production, both the frontend and the backend will be local, this will only be used for our development environment;
  • Access-Control-Allow-Methods, which is set to * by convenience. We could specify which methods would be permitted on our allowed domains, but we thought this wasn't necessary for a development env.

After setting these flags, I left the proxy configuration set on Webpack, as such:

    proxy: {
      '/api': {
        target: 'http://<ip>:8888',
        secure: false,
      },
    },
    headers: {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Headers': '*',
        'Access-Control-Allow-Methods': '*',
    },

On re-testing our requests and everything went smoothly. We found no way of doing this solely on the client side.

Thanks for the comments, everyone.

Solution 2

Exposing an API backend server from the same origin the web application is loaded helps to get around the CORS restriction. It's where the webpack dev-server proxy comes in handy. First, make sure requests to the API address the proxy. If XMLHttpRequest's or fetches in source code address absolute URLs (as opposed to relative ones) use a flag that indicates a development mode to choose the proxy as the origin. For example:

fetch(isDevelopment ? '/api' : 'http://backend.server.com/api')
.then(response => processResponse(response));

Second, configure the proxy to target the required API on the real backend server:

devServer: {
  proxy: {
    '/api': {
      target: 'http://backend.server.com'
    }
  }
}

There are no cross-origin requests from a browser's viewpoint anymore. This configuration may be also enough for the server to respond properly (for instance Jetty 9 with its CORS filter set works for me just fine). On the other hand, browsers may send Origin headers even with same-origin requests. To prevent CORS issues on different backend servers, if there is any, change the Origin header to match the target URL like this:

devServer: {
  proxy: {
    '/api': {
      target: 'http://backend.server.com',
      onProxyReq: proxyReq => {
        if (proxyReq.getHeader('origin')) {
          proxyReq.setHeader('origin', 'http://backend.server.com');
        }
      }
    }
  }
}

Refer to the documentation of dev-server and the http-proxy-middleware library it uses under the hood for more information. Also, TCP/HTTP sniffers like Wireshark are very helpful to understand what is sent where and what is sent in response.

Solution 3

Since no access-control-allow-origin header is present in response header, you can force one in your proxy settings:

proxy: {
  '/api': {
    target: 'http://<ip>:8888',
    secure: false,
    onProxyRes: response => {
        response.headers['access-control-allow-origin'] = 'http://localhost:8000';
    },
  },
},

It means that all proxied responses will have this header. This onProxyRes is http-proxy-middleware stuff, source: https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/proxy-events.md

Share:
11,367
gilneto8
Author by

gilneto8

Updated on June 05, 2022

Comments

  • gilneto8
    gilneto8 almost 2 years

    I have a simple ReactJS+Typescript application (bundled with Webpack) that I've been designing for the last couple of days. Now, I was starting the backend connection part (not my field, nor my responsability in this context), with which I'm having some difficulties. The main problem is a CORS error on the browser. This is my setup:

        "webpack": "^4.30.0",
        "webpack-dev-server": "^3.3.1",
        "typescript": "^3.4.5",
        "react": "^16.8.6",
        "axios": "^0.18.0",
        Chrome version 74.0.3729.131 (64-bit)
    

    Without any type of suggestion or experiment, this is the error I encountered: enter image description here

    I've tried a couple of things to solve this problem, but none of them actually fixed it. They are as follows:

    1. On my webpack.dev.ts configuration file, I configured the webpack proxy property. I configured it like this:
      devServer: {
        contentBase: path.join(__dirname, process.env.PUBLIC_PATH || '../dist'),
        compress: true,
        host: process.env.DEV_HOST || 'localhost',
        port: process.env.DEV_PORT || 8000,
        open: 'chrome',
        proxy: {
          '/api': {
            target: 'http://<ip>:8888',
            secure: false,
          },
        },
        headers: {
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Headers': '*',
            'Access-Control-Allow-Methods': '*',
        },
      },
    

    The request to the backend is built as follows:

    const config = {
      method: 'post',
      url: '/api/login',
      data: { password: password },
    };
    axios(config)
      .then(resp => console.log(resp))
      .catch(error => console.log(error));
    

    Please note that, for now, the password field is just a placeholder and is not validated on the backend, and as such, the Access-Control-Allow-Credentials header is not required.

    To my big disappointment, the request doesn't seem to be using the proxy configured, since this is the error: enter image description here

    On the network tab, there's also no reference to the backend IP address. Please note that only in this case does the request show as a POST and not as an OPTIONS request.

    1. Tried configuring axios with the headers required for CORS control on my side (client). This also didn't bring any solution to the table. This is what I came up with:
    const config = {
      method: 'post',
      url: 'http://<ip>:8888/api/login',
      data: { password: password },
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Headers': '*',
        'Access-Control-Allow-Methods': '*',
      },
    };
    axios(config)
      .then(resp => console.log(resp))
      .catch(error => console.log(error));
    

    The result that appeared is similar to the one without any configuration (first image attached here).

    1. Finally, I installed a Chrome extension for CORS, that (I suppose) attaches the Access-Control-Allow-Origin: * header to all requests. This is where things get interesting/weird. With this extension active (and without configuring it), even if I disable all the other previous options, the error changes:

    enter image description here

    I've also tried activating all three previous options at the same time, and this last error is what appears.

    I'm not sure where to head next, or how to proceed from here. I'm avoiding requesting a change to the server API's headers, but I'm keeping that option open as a last resort.

    I've tried to be thorough with the explanation and with what I want to achieve, but feel free to request more information.

    Thanks in advance.