I've been working with CORS and encountered the following issue.
Client complains about no 'Access-Control-Allow-Origin' header is present, while they are present, and client make the actual POST request and receives 200.
function initializeXMLHttpRequest(url) { //the code that initialize the xhr
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
//set headers
for (var key in headers) {
if (headers.hasOwnProperty(key)) { //filter out inherited properties
xhr.setRequestHeader(key,headers[key]);
}
}
return xhr;
}
In Chrome
chrome console log
Chrome OPTIONS request
Chrome POST request
In Firefox
Firefox Console Log
Firefox OPTIONS request
Firefox POST request
In short: Access control headers (e.g. Access-Control-Allow-Origin) need to present in response for both OPTIONS and actual POST.
Work Flow:
Client make OPTIONS request with those HTTP access headers. (e.g. Origin, Access-Control-Request-Method, Access-Control-Request-Headers)
Server respond with those access control headers, allowing access. (e.g. Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Allow-Credentials, Access-Control-Allow-Methods, Access-Control-Allow-Headers)
Client makes POST request with data.
Server respond to POST. If Access-Control-Allow-Origin header is NOT present in the server response. Although the POST is successful and shows 200 status code in network tab, xhr.status is 0 and xhr.onerror will be triggered. And browser would show up the error message.
Header References:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
Value null for Access-Control-Allow-Origin won't do, it has to be either the origin domain or * to allow any origin.
For more details, refer to MDN.
Related
I'm trying to get informations from a SystemLinkServlet.
So I tried to execute this JavaScript code from a Nintex Forms (Sharepoint) :
var http = new XMLHttpRequest();
var url = 'www.exampleservlet.com';
var params = "anyxml"
http.open('POST', url, true)
http.setRequestHeader('Content-type', 'application/xml');
http.setRequestHeader('Access-Control-Allow-Origin', '*');
http.onreadystatechange = function() {
if(http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
};
http.send(params);
But I still got this error in my console :
Access to XMLHttpRequest at 'www.exampleservlet.com' from origin 'www.exampleorigin.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
It seems that the header is ignored or maybe I can't set multiple request headers?
It works on Postman.
Update
So It worked with an extension but apparently, I can't set headers with JavaScript code in my Nintex Forms.
I'm trying to find to pass those headers without using an extension.
If you are using PHP, try adding the following code at the beginning of the php file:
If you are using localhost, try this:
header("Access-Control-Allow-Origin: *");
If you are using external domains such as server, try this:
header("Access-Control-Allow-Origin: http://www.webiste.com");
Also you I suggest you to use this extension:
https://chrome.google.com/webstore/detail/cors-unblock/lfhmikememgdcahcdlaciloancbhjino?hl=en
Postman or other similar tools provide you development environments. In this way, you can ignore and pass CORS rule while sending request and getting response by changing tool settings. But if you sending request via browser(chrome, firefox etc.), browsers always add some preflight controls.
For example, browser send options message to get server side rule before your http request. So that invalid or wrong requests are blocked by browser before processing your http request.
In your case, server side must include your domain information. You can not change this communication rule from client side by adding just "Access-Control-Allow-Origin: *" or "Access-Control-Allow-Origin: http://www.webiste.com" statements.
From: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests
The following is an example of a request that will be preflighted:
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://bar.other/resources/post-here/');
xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = handler;
xhr.send('<person><name>Arun</name></person>');
The example above creates an XML body to send with the POST request.
Also, a non-standard HTTP X-PINGOTHER request header is set. Such
headers are not part of HTTP/1.1, but are generally useful to web
applications. Since the request uses a Content-Type of
application/xml, and since a custom header is set, this request is
preflighted.
Will a preflight request be triggered if the request is same-origin but does not follow the header guidelines?
No, preflight requests are only done for cross-site requests. If you look at the beginning of the MDN article there is an image that explicitly says "Same origin-requets (always allowed)" as an example.
The below image isn't very visible if running SO in dark mode, if so, check the image in the article on the url below.
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
I am using the Pons dictionary API documented here: https://en.pons.com/assets/docs/api_dict.pdf
It works when I use a get request in my mac terminal, but not when using XMLHttpRequest in my javascript file (shown below) when testing it in my browser. It always gives me a 404 error and then states
"Access to XMLHttpRequest at 'https://api.pons.com/v1/dictionary?q=casa&l=dees' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status."
Any ideas?
I have tried making a request that does not require my X-Secret (credentials) and it works, but as soon as I set the X-Secret header it throws a 404 error. I need to set this heading for most request types.
Here is my code. I have censored my credentials in the X-Secret header.
var request = new XMLHttpRequest()
let url = new URL('https://api.pons.com/v1/dictionary');
url.searchParams.set('q', 'casa');
url.searchParams.set('l', 'dees');
request.open('GET', url);
//request.withCredentials = true;
request.setRequestHeader("X-Secret", "***");
request.setRequestHeader("Access-Control-Allow-Origin", "*");
request.setRequestHeader("Access-Control-Allow-Credentials", "true");
request.setRequestHeader("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
request.setRequestHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization");
request.send()
request.onload = function() {
// Begin accessing JSON data here
var data = JSON.parse(this.response)
if (request.status >= 200 && request.status < 400) {
console.log(data)
} else {
console.log('error')
}
}
CORS is "cross-origin resource sharing"; specifically, it's used to refer to policies that block requests between different domains. In this case, XHR is sending a "preflight request" -- that is, a request with verb OPTIONS (rather than GET, POST, etc.) -- to make sure the server will accept a real request. It will do this whenever you use non-standard (and some standard but optional) headers. The preflight request is coming back as 404; the server doesn't support OPTIONS to that endpoint.
The way to get around CORS is to use a proxy. That is, have a backend script (in Node, PHP, whatever) on your domain that sends the request to the other API server and echoes its response. CORS only applies to AJAX requests, not backend ones, so this will get around the issue, and it is the accepted technique.
I've got a simple code that sends files to server from https://app.myserver:4202 to https://myserver/backend/upload.php:
//script executed on`https://app.myserver:4202`
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://myserver.com/backend/upload.php");
xhr.withCredentials = true;
xhr.send(fd); //fd is formdata from file input
yay, it successfully sends the files with cookie so I can perform user authentication in my upload.php script. Now, I also want to display progress bar so I changed the code to this:
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://myserver.com/backend/upload.php");
xhr.withCredentials = true;
xhr.upload.addEventListener("progress", function(evt){
if (evt.lengthComputable) {
console.log("add upload event-listener" + evt.loaded + "/" + evt.total);
}
}, true);
xhr.send(fd);
Whoa, all hell broke loose! We have a CORS problem. Notice that I only added a progress tracker.
Access to XMLHttpRequest at 'https://myserver/backend/upload.php' from origin 'https://myserver:4202' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
I took great care to setup CORS correctly so this are myserver response headers that are relevant to CORS
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: origin, x-requested-with, content-type
Access-Control-Allow-Methods: DELETE, HEAD, GET, OPTIONS, POST, PUT
Access-Control-Allow-Origin: https://app.myserver.com:4202
Access-Control-Max-Age: 86400
They work well, if no progress info is desired, but fail when it is tracked.
So tell me, what am I missing out here?
We have a CORS problem. Notice that I only added a progress tracker.
This behaviour is defined in the spec:
Registering one or more event listeners on an XMLHttpRequestUpload object will result in a CORS-preflight request. (That is because registering an event listener causes the upload listener flag to be set, which in turn causes the use-CORS-preflight flag to be set.)
I took great care to setup CORS correctly so this are myserver response headers that are relevant to CORS
Read the error message carefully:
Access to XMLHttpRequest at 'https://myserver/backend/upload.php' from origin 'https://myserver:4202' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
So the HTTP response headers you set aren't really relevant.
The browser is making the OPTIONS request to ask for permission to make the POST request and the server is responding with something other that 200 OK.
Possibly it is sending 405 Method Not Allowed. Check the Network tab of your developer tools to find out exactly what the HTTP response status is, and then adjust the server accordingly.
Unable to make https XMLHttpRequest requests - getting this back:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://localhost:8443' is therefore not allowed access.
If I replace the url with www.google.com, it's fine, but https://www.google.com does the same thing.
It works when I use Postman/JaSON Chrome extensions, so I tried open -a Google\ Chrome --args --disable-web-security, but that didn't work either.
let request = new XMLHttpRequest();
request.open("POST", "https://outlook.office365.com/EWS/Exchange.asmx", true);
request.setRequestHeader("Authorization", "Bearer " + asyncResult.value);
request.setRequestHeader("Content-type", "text/xml; charset=utf-8");
request.setRequestHeader('Access-Control-Allow-Origin', '*');
request.setRequestHeader("Access-Control-Allow-Headers", "Content-type, Origin");
request.setRequestHeader("Access-Control-Allow-Methods", "POST");
request.onload = function() {
};
request.onerror = function() {
debugger;
}
request.send(requestBody);
The "Access-Control-Allow-Origin" header is set server-side for security reasons.
If you had access to the server you could just do:
header("Access-Control-Allow-Origin: *");
This, however, is not safe for obvious reasons. You should always allow as minimum external access as possible.
Recently I had the same issue. What I did was just use my own server, make the request from there to the api, and just have the script make the request to my server instead (from which you can set the access control header). So basically, instead of making the request to the third party client-side (from javascript), make it server-side and then get that data from your server via javascript.