1

I'm having CORS issues with a very simple app, took me about 10 minutes to code it and I've spent hours trying to fix this CORS error.

The frontend, written in React, makes a POST request to the Express backend using axios.

app.use(cors()) is called before the single route in my Express app.

And this is the axios request in my React app:

(await axios.post("http://localhost:3000/login", {username: username, password: password})).data.success

I'm getting this CORS error:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at...

enter image description here

OPTIONS request's header enter image description here

POST request's header enter image description here

Everything works fine on localhost though.

Express code:

require('dotenv').config();
const createError       = require('http-errors');
const express           = require('express');
const cors              = require('cors');
const compression       = require('compression');
const app               = express();

app.use(cors({origin: "*", allowedHeaders: ['Content-Type']}));
app.use(express.json());
app.use(compression());
app.use(express.urlencoded({extended: true}));
app.post("/login", (req, res) => {
    const params = req.body;
    if (params.username === process.env.USERNAME && params.password === process.env.PASSWORD)
        return res.json({success: true});
    else
        return res.status(401).json({success: false});
});

app.use((req, res, next) => next(createError(404)));
app.listen(process.env.NODE_PORT, () => console.log("Listening on port " + process.env.NODE_PORT));

Current, not ideal, working solution:

Use the machine's IP instead of localhost.

(await axios.post("http://192.168.*.*:3000/login", {username: username, password: password})).data.success 
Alexandre
  • 59
  • 1
  • 6
  • 1
    Can you show express code – Sean Feb 04 '22 at 19:27
  • Just added it in the question @SeanLawton – Alexandre Feb 04 '22 at 19:29
  • You can either exclude the local domain or just use [this extension](https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?hl=en) for development. – mamady Feb 04 '22 at 19:33
  • How can I exclude the local domain? @mamady – Alexandre Feb 04 '22 at 19:35
  • @Alexandre [This](https://stackoverflow.com/questions/43150051/how-to-enable-cors-nodejs-with-express) should help. – mamady Feb 04 '22 at 19:40
  • @mamady I already tried all that, OPTIONS and POST still choke with the same CORS error. – Alexandre Feb 04 '22 at 19:48
  • Fixed it by ditching `localhost` for my IP. – Alexandre Feb 04 '22 at 19:54
  • please provide the full cors error, CORS errors are very specific. – Kevin B Feb 04 '22 at 20:13
  • @KevinB `Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:3000/login. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 204.` – Alexandre Feb 04 '22 at 21:01
  • @KevinB `Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:3000/login. (Reason: CORS request did not succeed). Status code: (null).` – Alexandre Feb 04 '22 at 21:01
  • Huh, that's a new one for me, congratulations. MDN says that's a network error – Kevin B Feb 04 '22 at 21:03
  • Yep seen that, no idea where's the network error at though, and it is only being raised when I'm not on localhost. Anyway now it works fine even outside localhost since I changed the uri in the `axios` request. – Alexandre Feb 04 '22 at 21:16

3 Answers3

1

Problem

As can be gleaned from the Axios source code, when the second parameter to post is an object (which is the case, here), Axios sets the request's Content-Type header to application/json.

This header value is such as to cause the browser to trigger CORS preflight. The server must then explicitly list Content-Type in the Access-Control-Allow-Headers header of the response to the preflight request; otherwise, CORS preflight will fail, resulting in a CORS error in your browser.

Note that the default configuration of the Express CORS middleware (which you are currently relying on) doesn't allow any headers.

Solution

Explicitly allow Content-Type in your CORS configuration. You can do so with the allowedHeaders property of the CORS middleware's configuration object.

jub0bs
  • 60,866
  • 25
  • 183
  • 186
  • I've already tried that and it doesn't work. Still getting the same CORS error as before using `localhost`. `app.use(cors({origin: "*", allowedHeaders: ['Content-Type']}));` – Alexandre Feb 06 '22 at 18:37
  • @Alexandre Ok but, at the very least, you do need to allow the `Content-Type` header; that much is clear. To give us more information, please edit your question and add screenshots of the preflight request and response from your browser's Network tab. Also add a screenshot of the CORS error from your browser's Console tab. – jub0bs Feb 06 '22 at 19:38
  • I uploaded a screenshot of the CORS error and updated the current app configuration as suggested. – Alexandre Feb 07 '22 at 19:52
  • @Alexandre Your screenshots show the preflight and actual requests, but not the responses. We need both. – jub0bs Feb 07 '22 at 20:55
  • There aren't any responses for either request: `Response body is not available to scripts (Reason: CORS Failed)`. @jub0bs – Alexandre Feb 07 '22 at 21:11
  • If the actual (`POST`) request is sent by the browser, then CORS preflight must have succeeded, and there must have been a response to the preflight request. – jub0bs Feb 07 '22 at 21:14
  • This is the response for both the preflight and POST requests: `Response body is not available to scripts (Reason: CORS Failed)`. – Alexandre Feb 08 '22 at 09:22
  • There isn't anything else available. @jub0bs – Alexandre Feb 08 '22 at 09:22
  • @Alexandre `¯\_(ツ)_/¯` – jub0bs Feb 08 '22 at 09:32
  • My bad, I deleted a line of code after going over and over it. The POST doesn't go through, CORS chokes on OPTIONS and the response is `No response data available for this request`. @jub0bs – Alexandre Feb 08 '22 at 11:19
  • Perhaps your server (or reverse proxy?) drops the connection when it receives `OPTIONS` requests. I'm really not sure. Difficult to tell what's going on. – jub0bs Feb 08 '22 at 18:03
  • Pretty weird indeed, why would it drop the connection only when the front calls the back using `localhost` instead of using the server's IP? I tried running the app on macOS Monterey, Kali and LUbuntu (the app will be running on a LUbuntu VM for production), same error on each OS. I'll keep using the machine's IP for now which seems to be the only working solution. Thanks though! – Alexandre Feb 08 '22 at 20:09
-1

You need to configure your cors options. The origin can be a string or array which is a whitelist of the sources allowed to make requests. You can even handle it with a function if you need it to be dynamic. Also you can allow or disallow methods, validate credentials, etc. Check the package readme

Cors is a security mechanism to block any try between different domains. For example you have your backend in a domain like: https://wordpress.com and you want to fetch your frontend from your domain https://example.com this prevents that anyone can get access to your endpoints to steal information but this should not be your only security mechanism

const corsOptions = {
    origin: "http://localhost:3000",
    methods: ["GET","POST"]
};

app.use(cors(corsOptions));
  • Nope that's not it, `cors()` defaults to `{origin: "*"}` and handles preflight requests when not passed any options – Alexandre Feb 04 '22 at 19:41
-1

Use the machine IP instead of localhost.

await axios.post("http://192.168.*.*:3000/login", {username: username, password: password})).data.success` 
Alexandre
  • 59
  • 1
  • 6
  • Why did this help? an IP vs a domain would still be a different origin, if neither are the same domain/port/protocol as the page sending the request. – Kevin B Feb 04 '22 at 20:14
  • @KevinB Indeed it would. But the error wasn't actually a CORS misconfiguration (at least, I don't think so). The front was calling the back using localhost. Hence, a CORS error was being raised by the browser on each request. Changing localhost to the server's IP solved this issue. As you may have noticed I have no experience with CORS, thus I am not able to explain how this helped. – Alexandre Feb 04 '22 at 20:56
  • 1
    I see, it sounds like what happened then is by using the IP, you bypassed cors entirely by making it not necessary. – Kevin B Feb 04 '22 at 21:00
  • @KevinB I don't think I made cors unnecessary by using the IP. If it were made unnecessary, the `cors` configuration from the Express app could be deleted without a cors error being raised, but I still get a cors error after deleting the cors config: `XMLHttpRequest cannot load http://192.168.1.34:3000/login due to access control checks.` – Alexandre Feb 04 '22 at 21:40
  • I agree, i think you're right in terms of this is some form of misconfiguration somewhere unrelated to CORS. – Kevin B Feb 04 '22 at 21:41
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 05 '22 at 01:11