I'm trying to fetch some data from the REST API of HP Alm. It works pretty well with a small curl script - I get my data.
Now doing that with JavaScript, fetch and ES6 (more or less) seems to be a bigger issue. I keep getting this error message:
Fetch API cannot load . Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:3000' is therefore not allowed access. The response had HTTP status code 501. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I understand that this is because I am trying to fetch that data from within my localhost and the solution should be using CORS. Now I thought I actually did that, but somehow it either ignores what I write in the header or the problem is something else?
So, is there an implementation issue? Am I doing it wrong? I can't check the server logs unfortunately. I'm really a bit stuck here.
function performSignIn() {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');
headers.append('GET', 'POST', 'OPTIONS');
headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));
fetch(sign_in, {
//mode: 'no-cors',
credentials: 'include',
method: 'POST',
headers: headers
})
.then(response => response.json())
.then(json => console.log(json))
.catch(error => console.log('Authorization failed : ' + error.message));
}
I am using Chrome. I also tried using that Chrome CORS Plugin, but then I am getting another error message:
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'. Origin 'http://127.0.0.1:3000' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Remove this:
This error occurs when the client URL and server URL don't match, including the port number. In this case you need to enable your service for CORS which is cross origin resource sharing.
If you are hosting a Spring REST service then you can find it in the blog post CORS support in Spring Framework.
If you are hosting a service using a Node.js server then
Add following lines to your server.js
Using
dataType: 'jsonp'
worked for me.This answer covers a lot of ground, so it’s divided into three parts:
How to use a CORS proxy to get around “No Access-Control-Allow-Origin header” problems
If you don’t control the server your frontend JavaScript code is sending a request to, and the problem with the response from that server is just the lack of the necessary
Access-Control-Allow-Origin
header, you can still get things to work—by making the request through a CORS proxy. To show how that works, first here’s some code that doesn’t use a CORS proxy:The reason the
catch
block gets hit there is, the browser prevents that code from accessing the response which comes back fromhttps://example.com
. And the reason the browser does that is, the response lacks theAccess-Control-Allow-Origin
response header.Now, here’s exactly the same example but just with a CORS proxy added in:
Note: If https://cors-anywhere.herokuapp.com is down or unavailable when you try it, then see below for how to deploy your own CORS Anywhere server at Heroku in just 2-3 minutes.
The second code snippet above can access the response successfully because taking the request URL and changing it to https://cors-anywhere.herokuapp.com/https://example.com—by just prefixing it with the proxy URL—causes the request to get made through that proxy, which then:
https://example.com
.https://example.com
.Access-Control-Allow-Origin
header to the response.The browser then allows the frontend code to access the response, because that response with the
Access-Control-Allow-Origin
response header is what the browser sees.You can easily run your own proxy using code from https://github.com/Rob--W/cors-anywhere/.
You can also easily deploy your own proxy to Heroku in literally just 2-3 minutes, with 5 commands:
After running those commands, you’ll end up with your own CORS Anywhere server running at, e.g., https://cryptic-headland-94862.herokuapp.com/. So then rather than prefixing your request URL with
https://cors-anywhere.herokuapp.com
, prefix it instead with the URL for your own instance; e.g., https://cryptic-headland-94862.herokuapp.com/https://example.com.So if when you go to try to use https://cors-anywhere.herokuapp.com, you find it’s down (which it sometimes will be), then consider getting a Heroku account (if you don’t already) and take 2 or 3 minutes to do the steps above to deploy your own CORS Anywhere server on Heroku.
Regardless, whether you run your own or use https://cors-anywhere.herokuapp.com or other open proxy, this solution will work even if the request is one that triggers browsers to do a CORS preflight
OPTIONS
request—because in that case, the proxy also sends back theAccess-Control-Allow-Headers
andAccess-Control-Allow-Methods
headers needed to make the preflight successful.How to avoid the CORS preflight
The code in the question triggers a CORS preflight—since it sends an
Authorization
header.https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
Even without that, the
Content-Type: application/json
header would also trigger the preflight.What “preflight” means: before the browser tries the
POST
in the code in the question, it’ll first send anOPTIONS
request to the server — to determine if the server is opting-in to receiving a cross-originPOST
that includes theAuthorization
andContent-Type: application/json
headers.To properly test with
curl
, you need to emulate the preflightOPTIONS
request the browser sends:…with
https://the.sign_in.url
replaced by whatever your actualsign_in
URL is.The response the browser needs to see from that
OPTIONS
request must include headers like this:If the
OPTIONS
response doesn’t include those headers, then the browser will stop right there and never even attempt to send thePOST
request. Also, the HTTP status code for the response must be a 2xx—typically 200 or 204. If it’s any other status code, the browser will stop right there.The server in the question is responding to the
OPTIONS
request with a 501 status code, which apparently means it’s trying to indicate it doesn’t implement support forOPTIONS
requests. Other servers typically respond with a 405 “Method not allowed” status code in this case.So you’re never going to be able to make
POST
requests directly to that server from your frontend JavaScript code if the server responds to thatOPTIONS
request with a 405 or 501 or anything other than a 200 or 204 or if doesn’t respond with those necessary response headers.The way to avoid triggering a preflight for the case in the question would be:
Authorization
request header but instead (for example) relied on authentication data embedded in the body of thePOST
request or as a query parameterPOST
body to have aContent-Type: application/json
media type but instead accepted thePOST
body asapplication/x-www-form-urlencoded
with a parameter namedjson
(or whatever) whose value is the JSON dataHow to fix “Access-Control-Allow-Origin header must not be the wildcard” problems
For a request that includes credentials, browsers won’t let your frontend JavaScript code access the response if the value of the
Access-Control-Allow-Origin
response header is*
. Instead the value in that case must exactly match your frontend code’s origin,http://127.0.0.1:3000
.See Credentialed requests and wildcards in the MDN HTTP access control (CORS) article.
If you control the server you’re sending the request to, then a common way to deal with this case is to configure the server to take the value of the
Origin
request header, and echo/reflect that back into the value of theAccess-Control-Allow-Origin
response header. For example, with nginx:But that’s just one example; other (web) server systems provide similar ways to echo origin values.
That Chrome CORS plugin apparently just simplemindedly injects an
Access-Control-Allow-Origin: *
header into the response the browser sees. If the plugin were smarter, what it would be doing is setting the value of that fakeAccess-Control-Allow-Origin
response header to the actual origin of your frontend JavaScript code,http://127.0.0.1:3000
.So avoid using that plugin, even for testing. It’s just a distraction. If you want to test what responses you get from the server with no browser filtering them, you’re better off using
curl -H
as above.As far as the frontend JavaScript code for the
fetch(…)
request in the question:Remove those lines. The
Access-Control-Allow-*
headers are response headers. You never want to send them in a request. The only effect that’ll have is to trigger a browser to do a preflight.