XMLHttpRequest has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response

Viewed 4275

I am having issues creating new users from the form due to CORS. I was able to last week in this app but not sure what is missing from my server (methods, origin, headers, etc.)or my API call.

Here is what is suggested in the issues part of the console:

To fix this issue, include the additional request headers you want to use in the Access-Control-Allow-Headers response header of the associated preflight request. 1 request Request Status Preflight Request Disallowed Request Header new_user blocked new_user content-type

Here is the server code:

require('dotenv').config();
const express = require('express');
const cors = require('cors');

const app = express();

// Cookies:
const cookieParser = require('cookie-parser');

require('./config/mongoose.config');
app.use(cookieParser());

//required for post request
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// routes:
require('./routes/user.routes')(app);
require('./routes/spot.routes')(app);


// blocking cors errors:
const corsOptions = {
    origin: 'http://localhost:3000',
    methods: ["GET", "POST"],
    allowedHeaders: ["*"],
    credentials: true,            //access-control-allow-credentials:true
    optionSuccessStatus: 200,
}
app.use(cors(corsOptions)) // Use this after the variable declaration


//  MIDDLEWARE:
// app.use(cors(
//     { credentials: true, origin: 'http://localhost:3000' },
//     { headers: { "Access-Control-Allow-Origin": "*" } }));


// Middleware CORS API CALLS: 
app.use((req, res, next) => {
    if (req.method === "OPTIONS") {
        res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, GET", true);
        return res.status(200).json({});
    }
    next();
});

//listen on port:
app.listen(9000, () => {
    console.log("Listening at Port 9000")
})

Here are the routes:

const UserController = require('../controllers/user.controllers');
const { authenticate } = require('../config/jwt.config');
module.exports = function (app) {
    app.post('/api/new_user', authenticate, UserController.register);
    app.get('/api/users', UserController.getAllUsers);
    app.get('/api/users/:id', UserController.login);
    app.post('/api/users/logout', UserController.logout);
    app.put('/api/users/:id', UserController.updateUser);
    app.delete('/api/users/:id', UserController.deleteUser);
}

Here is the client (form code):

const onSubmitHandler = e => {
        e.preventDefault();

        const { data } =
            axios.post('http://localhost:9000/api/new_user', {
                userName,
                imgUrl,
                email,
                password,
                confirmPassword
            },
                { withCredentials: true, },
                // { headers: { 'Access-Control-Allow-Origin': '*' } }
                { headers: ["*"] }
            )
                .then(res => {
                    history.push("/dashboard")
                    console.log(res)
                    console.log(data)
                })
                .catch(err => console.log(err))

I've done a bit of research and not sure if I should make a proxy, use a plugin, etc, but I could use the extra eyes. Thanks all!

2 Answers

If you're already using the cors middleware, you don't need to manually handle OPTIONS requests, it does that for you.

Remove this section...

// Middleware CORS API CALLS: 
app.use((req, res, next) => {
    if (req.method === "OPTIONS") {
        res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, GET", true);
        return res.status(200).json({});
    }
    next();
});

You should also register the cors middleware before your routes, along with your other middleware.

app.use(cors({
  origin: "http://localhost:3000",
  credentials: true,            //access-control-allow-credentials:true
  optionSuccessStatus: 200,
}))

//required for post request
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// routes:
require('./routes/user.routes')(app);
require('./routes/spot.routes')(app);

On the client-side, ["*"] is an invalid request header and needs to be removed. You're also not handling the async response correctly. It should be

axios.post("http://localhost:9000/api/new_user", {
  userName,
  imgUrl,
  email,
  password,
  confirmPassword
}, { withCredentials: true, }).then(res => {
  history.push("/dashboard")
  console.log(res)
  console.log(res.data) //  this is where `data` is defined
}).catch(console.error)

I think that it is caused by this line return res.status(200).json({}); When you respond to the CORS pre-flight you are not supposed to include a Content-Type and setting the return type to JSON probably does exactly this.

Try

res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, GET", true);
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
return res.status(200).end();
Related