361

I have a setup involving

Frontend server (Node.js, domain: localhost:3000) <---> Backend (Django, Ajax, domain: localhost:8000)

Browser <-- webapp <-- Node.js (Serve the app)

Browser (webapp) --> Ajax --> Django(Serve ajax POST requests)

Now, my problem here is with CORS setup which the webapp uses to make Ajax calls to the backend server. In chrome, I keep getting

Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.

doesn't work on firefox either.

My Node.js setup is:

var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', 'http://localhost:8000/');
    res.header('Access-Control-Allow-Credentials', true);
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
};

And in Django I'm using this middleware along with this

The webapp makes requests as such:

$.ajax({
    type: "POST",
    url: 'http://localhost:8000/blah',
    data: {},
    xhrFields: {
        withCredentials: true
    },
    crossDomain: true,
    dataType: 'json',
    success: successHandler
});

So, the request headers that the webapp sends looks like:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: "Origin, X-Requested-With, Content-Type, Accept"
Access-Control-Allow-Methods: 'GET,PUT,POST,DELETE'
Content-Type: application/json 
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: csrftoken=***; sessionid="***"

And here's the response header:

Access-Control-Allow-Headers: Content-Type,*
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE
Content-Type: application/json

Where am I going wrong?!

Edit 1: I've been using chrome --disable-web-security, but now want things to actually work.

Edit 2: Answer:

So, solution for me django-cors-headers config:

CORS_ORIGIN_ALLOW_ALL = False
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
    'http://localhost:3000' # Here was the problem indeed and it has to be http://localhost:3000, not http://localhost:3000/
)
ixaxaar
  • 4,821
  • 3
  • 22
  • 31
  • 2
    For me it is localhost:3000 without http, like this: CORS_ORIGIN_WHITELIST = ( 'localhost:3000', ) – Andrei Aug 03 '17 at 15:36
  • Do you mean you use develop the frontend and backend in one PC? – fanhualuojin154873 Dec 12 '17 at 04:34
  • how about the frontend and backend in different PC? – fanhualuojin154873 Dec 12 '17 at 10:59
  • @ixaxaar why you say with the http works for you? we all only ` 'localhost:3000'` works. – 244boy Mar 16 '18 at 08:41
  • @244boy yeah the point is not the `http`, it is the `/` at the end. I suppose omitting http could work, but I've not really worked on this stuff for some years, so don't really know what works now! – ixaxaar Mar 17 '18 at 21:02
  • omitting `http://` just throws: `ERRORS: ?: (corsheaders.E013) Origin 'localhost:3000' in CORS_ORIGIN_WHITELIST is missing scheme or netloc` in Django 2.2 – nerdoc May 21 '19 at 14:55
  • Spent hours debugging this - and this thread was the fix: https://stackoverflow.com/questions/35713682/socket-io-gives-cors-error-even-if-i-allowed-cors-it-on-server – ingage Oct 24 '20 at 18:40

9 Answers9

288

This is a part of security, you cannot do that. If you want to allow credentials then your Access-Control-Allow-Origin must not use *. You will have to specify the exact protocol + domain + port. For reference see these questions :

  1. Access-Control-Allow-Origin wildcard subdomains, ports and protocols
  2. Cross Origin Resource Sharing with Credentials

Besides * is too permissive and would defeat use of credentials. So set http://localhost:3000 or http://localhost:8000 as the allow origin header.

Community
  • 1
  • 1
user568109
  • 43,824
  • 15
  • 87
  • 118
  • 56
    But what if there's more than one domain? – aroth Oct 08 '14 at 00:57
  • 13
    @aroth You can give a list of domains. Related question: http://stackoverflow.com/questions/1653308/access-control-allow-origin-multiple-origin-domains – user568109 Oct 08 '14 at 07:19
  • What if I'm getting the same error message but there is no `Access-Control-Allow-Headers` in the response? – Andy Dec 04 '15 at 00:09
  • 13
    @user568109 Could you explain "Besides `*` is too permissive and would defeat use of credentials."? – Hugo Wood Jun 24 '16 at 14:13
  • 13
    What is the "exact domain" if the request comes from mobile device, like it can happen with Cordova? – Christian Jul 02 '16 at 12:36
  • 1
    Ok, so how exactly does the server know whether or not it should send "*"? – Michael Oct 28 '16 at 00:56
  • Is it possible to provide localhost of a different computer than the server? I got this error: "The 'Access-Control-Allow-Origin' header has a value 'http://localhost:3000/' that is not equal to the supplied origin. Origin 'http://localhost:3000' is therefore not allowed access." – Avi Kaminetzky Aug 28 '17 at 21:28
  • 1
    @HugoWood, a man in the middle attack would cause one to send credentials to any (*) server – Cyril CHAPON Apr 16 '18 at 08:30
  • 11
    @Christian kinda old, but if anyone still curious, this problem happens only for applications running on browsers, because this error is thrown by the browser for security reasons. Other clients such as a mobile app, postman or any other backend code using http client to make a request won't have this problem, so you don't have to worry about the origin and the **exact domain**. – Alisson Apr 10 '19 at 05:19
  • 3
    For more than one domain use it like this response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); – Shivam Yadav Jan 30 '20 at 07:33
53

If you are using CORS middleware and you want to send withCredential boolean true, you can configure CORS like this:

var cors = require('cors');    
app.use(cors({credentials: true, origin: 'http://localhost:3000'}));
BSMP
  • 3,862
  • 8
  • 31
  • 41
Hamid
  • 2,272
  • 1
  • 25
  • 41
19

If you are using express you can use the cors package to allow CORS like so instead of writing your middleware;

var express = require('express')
, cors = require('cors')
, app = express();

app.use(cors());

app.get(function(req,res){ 
  res.send('hello');
});
Bulkan
  • 2,367
  • 1
  • 16
  • 31
  • 12
    Ah, now that's more convenient, however, the result's the same :( BTW, I'm using `app.use(cors({credentials: true}));` – ixaxaar Nov 02 '13 at 15:34
  • 1
    You might want to look into [this](https://github.com/ottoyiu/django-cors-headers) Django CORS middleware that is tested. – Bulkan Nov 02 '13 at 16:18
  • 1
    So you have two Django middlewares ? I would only use `django-cors-header` app. Make sure you add _localhost_ to `CORS_ORIGIN_WHITELIST` setting and set `CORS_ALLOW_CREDENTIALS` to `True` – Bulkan Nov 02 '13 at 16:26
  • 1
    Yeah man, tried that before to no avail, had `CORS_ORIGIN_ALLOW_ALL = True`, `CORS_ORIGIN_WHITELIST = ( 'localhost' )` and `CORS_ALLOW_CREDENTIALS = True` I get these headers: `Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: http://localhost:3000/ Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE Content-Type: application/json` – ixaxaar Nov 02 '13 at 16:36
  • 1
    BTW, I was using django-cors-headers, but things were not working, so I coupled it with a custom middleware as well. – ixaxaar Nov 02 '13 at 16:39
  • 5
    After reading this documentation: https://github.com/expressjs/corsuse i using this config: app.use(cors({credentials: true, origin: 'http://localhost:3001'})); is working for me. – allel Feb 05 '16 at 08:38
  • 1
    @allel - that url is no more in use - find the following working url `https://github.com/expressjs/cors` – Abilash Arjunan Jul 12 '17 at 18:38
17

try it:

const cors = require('cors')

const corsOptions = {
    origin: 'http://localhost:4200',
    credentials: true,

}
app.use(cors(corsOptions));
Thomas Smyth
  • 3,953
  • 5
  • 22
  • 33
Iron shield
  • 291
  • 4
  • 9
16

Expanding on @Renaud idea, cors now provides a very easy way of doing this:

From cors official documentation found here:

" origin: Configures the Access-Control-Allow-Origin CORS header. Possible values: Boolean - set origin to true to reflect the request origin, as defined by req.header('Origin'), or set it to false to disable CORS. "

Hence we simply do the following:

const app = express();
const corsConfig = {
    credentials: true,
    origin: true,
};
app.use(cors(corsConfig));

Lastly I think it is worth mentioning that there are use cases where we would want to allow cross origin requests from anyone; for example, when building a public REST API.

NOTE: I would have liked to leave this as a comment on his answer, but unfortunately I don't have the reputation points.

  • I like this answer! especially about the fact that there are use cases where you want to allow all origins (many answers here seem to assume that it is always a bad practice). – Quentin C May 19 '21 at 17:28
13

If you want to allow all origins and keep credentials true, this worked for me:

app.use(cors({
  origin: function(origin, callback){
    return callback(null, true);
  },
  optionsSuccessStatus: 200,
  credentials: true
}));
Squirrl
  • 3,870
  • 9
  • 41
  • 75
  • @TSlegaitis Haha yeah that's why it works for all origins but keeps credentials. I wouldn't recommend it for security but it does work. – Squirrl Feb 15 '20 at 16:28
5

This works for me in development but I can't advise that in production, it's just a different way of getting the job done that hasn't been mentioned yet but probably not the best. Anyway here goes:

You can get the origin from the request, then use that in the response header. Here's how it looks in express:

app.use(function(req, res, next) {
  res.header('Access-Control-Allow-Origin', req.header('origin') );
  next();
});

I don't know what that would look like with your python setup but that should be easy to translate.

Renaud
  • 4,193
  • 7
  • 36
  • 61
  • [Mozilla Dev Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Access-Control-Allow-Origin) expand on the idea of changing the allowed origin to the one from the request. It is suggested to add a 'Vary: Origin' HTTP response header and whitelist allowed domains. – Ramzis Jun 10 '20 at 16:04
3

(Edit) The previously recomended add-on is not available any longer, you may try this other one


For development purposes in Chrome, installing this add on will get rid of that specific error:

Access to XMLHttpRequest at 'http://192.168.1.42:8080/sockjs-node/info?t=1546163388687' 
from origin 'http://localhost:8080' has been blocked by CORS policy: The value of the 
'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' 
when the request's credentials mode is 'include'. The credentials mode of requests 
initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

After installing, make sure you add your url pattern to the Intercepted URLs by clicking on the AddOn's (CORS, green or red) icon and filling the appropriate textbox. An example URL pattern to add here that will work with http://localhost:8080 would be: *://*

eriel marimon
  • 764
  • 10
  • 24
0

Had this problem with angular, using an auth interceptor to edit the header, before the request gets executed. We used an api-token for authentification, so i had credentials enabled. now, it seems it is not neccessary/allowed anymore

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    req = req.clone({
      //withCredentials: true, //not needed anymore
      setHeaders: {
        'Content-Type' : 'application/json',
        'API-TOKEN' : 'xxx'
      },
    });
    
    return next.handle(req);
  }

Besides that, there is no side effects right now.