node.jsexpresscorsresponse-headerspreflight

How to manually add response Access-Control-Allow headers to preflight requests


I have an express server ("express": "^4.19.2") set up and use this custom function to add response headers:

const allowCORS = (req: Request, res: Response, next: NextFunction): void => {
  const origin = req.get('origin');
  console.log('origin in allowCORS', origin);
  res.setHeader("Access-Control-Allow-Origin", origin || '*');
  res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");

  if (req.method === 'OPTIONS') {
    res.sendStatus(200);
  } else {
    next();
  }
};

In my route I do:

app.get("/", allowCORS, (req: Request, res: Response) => {
  res.send({response: "My root route"});
});

It works perfectly fine for all requests, except the prefleight request.

When I test a preflight request I get the status 200 but not the headers.

Here is my curl output:

>curl -i -X OPTIONS -H "Origin: https://some-frontend-application.com" -H "Access-Control-Request-Method: GET" -H "Access-Control-Request-Headers: Origin, X-Requested-With, Content-Type, Accept" http://localhost:3001/

HTTP/1.1 200 OK
X-Powered-By: Express
Allow: GET,HEAD
Content-Type: text/html; charset=utf-8
Content-Length: 8
ETag: W/"8-ZRAf8oNBS3Bjb/SU2GYZCmbtmXg"
Date: Wed, 14 Aug 2024 18:35:52 GMT
Connection: keep-alive
Keep-Alive: timeout=5

When I enable cors() for all routes:

import cors from 'cors';
app.use(cors())

I get the response headers for the preflight request:

>curl -i -X OPTIONS -H "Origin: https://some-frontend-application.com" -H "Access-Control-Request-Method: GET" -H "Access-Control-Request-Headers: Origin, X-Requested-With, Content-Type, Accept" http://localhost:3001/

HTTP/1.1 204 No Content
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
Vary: Access-Control-Request-Headers
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept
Content-Length: 0
Date: Wed, 14 Aug 2024 18:35:36 GMT
Connection: keep-alive
Keep-Alive: timeout=5

Also, for every other request the headers are set:

>curl -i -H "Origin: https://some-frontend-app.com" http://loca
lhost:3001/

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: https://some-frontend-app.com
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Content-Type: application/json; charset=utf-8
Content-Length: 42
ETag: W/"2a-nitr8TuSR9eKLHj8wLq+IXZALnA"
Date: Wed, 14 Aug 2024 18:43:01 GMT
Connection: keep-alive
Keep-Alive: timeout=5

Any idea how to fix this for the preflight?


Solution

  • Notice this line of code you have written, problem lies here.

    app.get("/", allowCORS, (req: Request, res: Response) => {
      res.send({response: "My root route"});
    });
    

    Its the method app.get, your allowCors middleware runs only during GET request but not during preflight. If you use cors package here result will be same. Thus this line never true.

      if (req.method === 'OPTIONS') {
    

    Solution

    Include middleware during preflight

    1. Apply middle only to that route

    app.options('/test', allowCors)
    app.get('/test', allowCors, handleTest) // or app.get('/test', cors(), handleTest)
    

    much better solution is app.route() (same as above but in elegant way)

    app.route('/test')
        .all(allowCors) // or .all(cors())
        .get(handleTest)
    

    2. Apply middleware for all

    app.use(allowCors)