81

I'm trying to allow CORS in node.js but the problem is that I can't set * to Access-Control-Allow-Origin if Access-Control-Allow-Credentials is set.

Also the specification said I can't do an array or comma separated value for Access-Control-Allow-Origin and the suggested method would be to do something similar to this Access-Control-Allow-Origin Multiple Origin Domains?

But I can't seem to do this way in node.js

["http://example.com:9001", "http://example.com:5001"].map(domain => {
  res.setHeader("Access-Control-Allow-Origin", domain);
});
res.header("Access-Control-Allow-Credentials", true);

The problem here is that it's bein override by the last value in the array, so the header will be set to res.setHeader("Access-Control-Allow-Origin", "http://example.com:5001");

Error from the client browser:

XMLHttpRequest cannot load http://example.com:9090/api/sync. The 'Access-Control-Allow-Origin' header has a value 'http://example.com:5001' that is not equal to the supplied origin. Origin 'http://example.com:9001' is therefore not allowed access.

tanguy_k
  • 8,999
  • 4
  • 46
  • 50
Ali
  • 8,267
  • 18
  • 66
  • 101

4 Answers4

214

Here is what I use in my express application to allow multiple origins

app.use((req, res, next) => {
  const allowedOrigins = ['http://127.0.0.1:8020', 'http://localhost:8020', 'http://127.0.0.1:9000', 'http://localhost:9000'];
  const origin = req.headers.origin;
  if (allowedOrigins.includes(origin)) {
       res.setHeader('Access-Control-Allow-Origin', origin);
  }
  //res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:8020');
  res.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.header('Access-Control-Allow-Credentials', true);
  return next();
});
tanguy_k
  • 8,999
  • 4
  • 46
  • 50
chank
  • 2,926
  • 1
  • 12
  • 22
  • Nice! This is exactly what i needed! – Scotty May 31 '16 at 11:34
  • 10
    Is there a difference between res.header and res.setHeader? – David Dal Busco Mar 26 '17 at 12:50
  • query: what is the reason you write `return` before the `next()`, isn't next called automatically and send request to next middleware? – diEcho May 20 '17 at 11:30
  • 1
    @pro.mean the `return next()` here will ensure that `app.use` will return whatever the `next` callback returns. just `next()` would still invoke the callback, but won't provide whatever the callback's return value is to the caller of `app.use`. you can't know if it's necessary or not unless you know what calls `app.use` down-stack, and what the `next` handling function is to be up-stack. – Benny Aug 14 '17 at 02:48
  • Wouldn't make more sense to make this middle ware of the options method like so, instead of intercepting every call. Remember this only applies to CORS requests which use preflight : app.options("*", function(req, res, next) {... –  Jun 20 '18 at 20:10
  • This answer works well. I also tried using express middlware cors for easy handling. https://expressjs.com/en/resources/middleware/cors.html – master_dodo Jul 16 '19 at 21:21
  • my `req.headers.origin` was `undefined` – Dylan Nov 21 '19 at 08:32
  • wow! This answer solved my issue. Thanks – Shashank Mar 27 '20 at 11:52
  • ```Access-Control-Allow-Credentials``` should be set true only for allowedOrigins isn't it? Since ```Access-Control-Allow-Credentials``` also requires the ```Access-Control-Allow-Origin``` to have the origin name instead of any wild card which , here, is only being done for allowedOrigins? – d_bhatnagar Aug 09 '20 at 18:45
  • You saved me! Thank you very much – NGabioud Aug 26 '20 at 19:20
  • This header should also be added: `res.setHeader('Vary', 'Origin');` That's mentioned in [Mozilla's docs for Access-Control-Allow-Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin#CORS_and_caching) : "If the server sends a response with an Access-Control-Allow-Origin value that is an explicit origin (rather than the "*" wildcard), then the response should also include a Vary response header with the value Origin — to indicate to browsers that server responses can differ based on the value of the Origin request header." – Chris G Dec 14 '20 at 22:01
14

Not sure if this is to late but I solved it by setting: res.setHeader("Access-Control-Allow-Origin", req.headers.origin);

This will simply allow every connection as the headers.origin will be sent with every query.

You may want to write a function to check if the req.headers.origin is a whitelisted domain (from a hardcoded array) and the simply return this domain if it exists in the array.

Matt
  • 447
  • 3
  • 6
  • 24
    This is an overly complicated way of saying res.setHeader("Access-Control-Allow-Origin", "*"); – Alexander Gonchiy Nov 10 '16 at 09:43
  • 20
    @AlexanderGonchiy no it's not. As a matter of fact it's completely different, accepting everything vs setting it dynamically to one single origin. Take credentials for example. If you want to allow credentials then your `Access-Control-Allow-Origin` can't use `*` but it will still work with this solution. Thanks for the post – cviejo Jul 10 '17 at 13:23
  • @cviejo You are correct. This was my exact use case. – Matt Jul 10 '17 at 19:33
  • 3
    A warning to people that come across this answer; this is almost always a serious security vulnerability. At best it's the same as Access-Control-Allow-Origin: *. If other CORS headers are used this could open up significant cross-site request forgery vulnerabilities. Vulnerability tracking databases (e.g. CVE) have a large number of vulnerabilities matching exactly what this answer recommends. – ShadowChaser Jul 08 '20 at 18:03
7

Check your whitelist against what your req.headers.origin e.g.

var origins = ['a.com', 'b.com', 'c.com', 'boobies.com'];
for(var i=0;i<origins.length;i++){
    var origin = origins[i];
    if(req.headers.origin.indexOf(origin) > -1){ 
         res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
         return;
    }
    // else, tough cookies. 
}

Enjoy.

Ross The Boss
  • 493
  • 4
  • 16
7

Here's a simple middleware function to serve up the correct CORS header from a whitelist. Setting this near the top of your express app will allow all your routes to set the proper header from the whitelist before serving up content.

app.use(function(req, res, next){
  var whitelist = ['localhost:4000', 'localhost:3000', 'anydomain.com']
  var host = req.get('host');

  whitelist.forEach(function(val, key){
    if (host.indexOf(val) > -1){
      res.setHeader('Access-Control-Allow-Origin', host);
    }
  })

  next();
});
Alan L.
  • 340
  • 3
  • 6
  • 2
    Not sure if things have changed since 2015 but when I inspect `req`, the value for `req.get('host')` is the URL for the server itself. If the request is coming from outside the server, which is why we need a whitelist, `req.get('origin')` or `req.get('referrer')` store the URL from where the request was made. The only difference I saw between the two is that `referrer` includes a trailing slash. – samurai_jane Mar 20 '19 at 20:22