I'm building a Backbone.js based app and face a strange issue.
At a certain point the app requests a collection resource and inside Chrome (and Safari) I get an error like that:
XMLHttpRequest cannot load http://api.mydomain.net/v1/foos/00000d/bars/000014/boots Origin http://localhost:3501 is not allowed by Access-Control-Allow-Origin.
Ok, CORS issue I thought and blamed my API. Then requested this very resource via CURL:
curl -i -H'Accept: application/json' -H'X-Auth-Token: pAWp5hrCmXA83GgFzgHC' -XOPTIONS 'http://api.mydomain.net/v1/foos/00000d/bars/000014/boots'
HTTP/1.1 200 OK
Status: 200
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Auth-Token
Content-Length: 0
looks good, now the GET:
curl -i -H'Accept: application/json' -H'X-Auth-Token: pAWp5hrCmXA83GgFzgHC' -XGET 'http://api.mydomain.net/v1/foos/00000d/bars/000014/boots'
HTTP/1.1 204 No Content
Status: 204
Cache-Control: no-cache
Content-Length: 0
Content-Type: text/plain
In case I request boots collection that contain at least one object, everything works fine. The CORS headers my server responds with arr totally fine as I think. So why do the browsers report a cross origin resource problem?
Is it due to the content type text/plain
of my 204 responses?
Preflight (OPTIONS) request in dev tools:
Request headers of aborted response:
Prevent sending the post data, if it wont be processed This is the only reason what is valid. Using options request will prevent sending the post data to the server unnecessarily.
The OPTIONS request is so called pre-flight request, which is part of Cross-origin resource sharing (CORS). Browsers use it to check if a request is allowed from a particular domain as follows: The browser wants to send a request, let's say a POST request with the application/json content type.
A preflight request is a small request that is sent by the browser before the actual request. It contains information like which HTTP method is used, as well as if any custom HTTP headers are present. The preflight gives the server a chance to examine what the actual request will look like before it's made.
The HTTP OPTIONS method requests permitted communication options for a given URL or server. A client can specify a URL with this method, or an asterisk ( * ) to refer to the entire server. Request has body. No.
You have to also include the Access-Control-Allow-Origin
in the response headers of the second request. That's not a client-side issue, but a backend one.
This behaviour is in accordance with the CORS specification, applied in the following explanation (section 7.1.5 "Cross-Origin Request with Preflight"):
Your request already fails at the first step of the resource sharing check:
Access-Control-Allow-Origin
header values, return fail and terminate this algorithm.I provide a simple NodeJS example illustrating your problem.
Your current backend behaves like:
require('http').createServer(function(request, response) {
if (request.method == 'OPTIONS') { // Handle preflight
response.writeHead(200, {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "X-Foo"
});
} else { // Handle actual requests
response.writeHead(204, {
//"Access-Control-Allow-Origin": "*"
});
}
response.end();
}).listen(12345);
Now, make the request and experience a failure:
var x = new XMLHttpRequest;
x.open('GET', 'http://localhost:12345');
x.setRequestHeader('X-Foo','header to trigger preflight');
x.send();
Go back to the code I provided, and enable the Access-Control-Allow-Origin
header in the response, and test again. Your request will now succeed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With