I want to use jQuery to run an asynchronous post with data to "[domain1]/first.php" on server #1 when a user on a page "[domain2]/unrelated.php" on server #2 clicks a link to "[domain2]/second.php?var1=data1?var2=data2" that's on server #2. I don't want to wait for "[domain1]/first.php" to finish or return back any response whatsoever.
"[domain1]/first.php" is set up to process data (it also does a cURL to "[domain2]/unrelated2.php" on server #1) and not echo anything, and to finish with an "exit;" or "?>" line.
"[domain2]/second.php" processes the data and echoes a page.
It works in Chrome, Opera and Firefox when it's synchronous. (but it's slow because of data processing, I want to avoid the wait after posting and before it follows the clicked link)
It works in Chrome or Opera when it's asynchronous.
Firefox gives a CORS error in the console for "[domain1]/first.php" when it's asynchronous:
"Cross-Origin Request Blocked: The Same Origin Policy disallows
reading the remote resource at [domain1]/first.php. This can be fixed
by moving the resource to the same domain or enabling CORS."
I've tried adding this to the top of "[domain1]/first.php":
// Allow from any origin
if (isset($_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // cache for 1 day
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
//exit(0);
}
The jQuery AJAX call on "[domain2]/unrelated.php" is:
$.ajax({
url: '[domain1]/first.php',
type: 'POST',
data: parameters
});
All three browsers used for testing are up to date. The jQuery in use is 1.7.2.
I could detect Firefox and only make it use a synchronous post, but it hardly seems satisfying considering it works asynchronously in other two browsers.
Any thoughts or suggestions?
I could post how the request and response headers look like, but I'm unable to do so right now, I'll edit them in at a later time, I apologize.
From searching around, this seems to primarily be a problem of the header that server #2 sends back in response from the original post from server #1, so I assume posting those headers is critical in solving this case?
Maybe I have to uncomment the commented "//exit(0);"?
Thanks for your time!
Related
I am trying to call a PHP API running on localhost:8000 from a React app running on localhost:3000. After many tries I am still getting "CORS Preflight Did Not Succeed" error.
Sent from the React app:
Sent from the devtools:
My API has following headers:
if (#$_SERVER['HTTP_ORIGIN']) {
header("Origin: http://localhost:8000");
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Max-Age: 1000');
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
}
I call the API with fetch like this (but it somehow sends empty request body):
let inputData:object = {email, password}
fetch("http://localhost:8000/data/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(inputData)
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
The strange thing is that the requests are working normally when sent directly from the browser devtools (2nd screenshot) or API clients like Insomnia:
Problem
Your first screenshot indicates that the response to the preflight request has status code 404. However, a necessary condition for CORS preflight to succeed is an ok status (i.e. a status in the range 2xx). See the relevant section (3.2.3) of the Fetch standard:
A successful HTTP response, i.e., one where the server developer intends to share it, to a CORS request can use any status, as long as it includes the headers stated above with values matching up with the request.
A successful HTTP response to a CORS-preflight request is similar, except it is restricted to an ok status, e.g., 200 or 204.
(my emphasis)
Solution
Make sure your server responds with a 2xx status to preflight requests that are meant to succeed.
Additional remarks
Allowing the Origin header is never necessary, simply because it's set by the user agent. You can drop Origin from the value of the Access-Control-Allow-Headers response header.
Why you're setting an Origin header in the response is unclear... Origin is a request header. You should be able to drop that header("Origin: http://localhost:8000"); line.
Instead of "manually" implementing CORS (which is error-prone), you should consider using a proven CORS middleware.
Your cors origin must be localhost:3000.
header("Origin: http://localhost:3000");
Because your frontend running on 3000.
Where the request comes from should be added as cors definition.
Make sure you are not outputting anything in php before returning the response to the frontend application. A simple echo "test"; or a print_r, vardump etc. can trigger this error.
Also, make sure there are no empty lines before the opening <?php tags since they send a premature response to the frontend that may cause this error.
Even though I set the headers in the file functions.php, the error keeps appearing, I tried with several different hooks:
function add_cors_http_header()
{
if (isset($_SERVER['HTTP_ORIGIN'])) {
// Decide if the origin in $_SERVER['HTTP_ORIGIN'] is one
// you want to allow, and if so:
// header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // cache for 1 day
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
// may also be using PUT, PATCH, HEAD etc
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT");
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
exit(0);
}
}
add_action('init', 'add_cors_http_header');
add_action('send_headers', 'add_cors_http_header');
add_action('rest_pre_serve_request', 'add_cors_http_header');
(Not all add_action at the same time)
And I also tried without the isset and in the header.php
EDIT ---
As requested, the client that is running is Vanilla Javascript, I made the same fetch on Postman and it worked, it's about joining a user to a guild in discord, hre is the code:
const args = JSON.stringify({
access_token: token,
});
const response = await fetch(
`https://discord.com/api/guilds/${this.guildID}/members/${userID}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `BOT ${this.bot_token}`,
},
body: args,
}
);
Access-Control-Allow-Origin (and similar headers) are for the server to define, and not the client. In this case, the server is the Discord API.
I'm guessing the confusion here is that your client is JavaScript, but you're trying to modify the headers in PHP. I think you should try to add the headers in JavaScript directly. Perhaps there's an Origin header you need?
I suggest looking at your Postman headers being sent and add them right in your JavaScript. For example:
headers: {
"Content-Type": "application/json",
Authorization: `BOT ${this.bot_token}`,
Origin: "http://localhost",
},
If that doesn't work, open your Chrome Dev Tools, open the Network tab, and look at the outgoing request, and see what headers it is sending and compare them with Postman. If Postman can work, just repeat what it is doing.
Regarding the WordPress function, I think you can remove it entirely as JavaScript is the client, not PHP/WordPress.
I'm trying to send some HTTP requests from my angular.js application to my server, but I need to solve some CORS errors.
The HTTP request is made using the following code:
functions.test = function(foo, bar) {
return $http({
method: 'POST',
url: api_endpoint + 'test',
headers: {
'foo': 'value',
'content-type': 'application/json'
},
data: {
bar:'value'
}
});
};
The first try ended up with some CORS errors. So I've added the following lines to my PHP script:
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT');
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding, X-Auth-Token, content-type');
The first error is now eliminated.
Now the Chrome's developer console shows me the following errors:
angular.js:12011 OPTIONS http://localhost:8000/test (anonymous
function)
423ef03a:1 XMLHttpRequest cannot load
http://localhost:8000/test. Response for preflight has invalid HTTP
status code 400
and the network request looks like I expected (HTTP status 400 is also expected):
I can't imagine how to solve the thing (and how to understand) why the request will send on localhost as OPTIONS and to remote servers as POST. Is there a solution how to fix this strange issue?
TL;DR answer
Explanation
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 to a particular URL, let's say a POST request with the application/json content type
First, it sends the pre-flight OPTIONS request to the same URL
What follows depends on the pre-flight request's response HTTP status code:
If the server replies with a non-2XX status response, the browser won't send the actual request (because he knows now that it would be refused anyway)
If the server replies with a HTTP 200 OK (or any other 2XX) response, the browser will send the actual request, POST in your case
Solution
So, in your case, the proper header is present, you just have to make sure the pre-flight request's response HTTP status code is 200 OK or some other successful one (2XX).
Detailed Explanation
Simple requests
Browsers are not sending the pre-flight requests in some cases, those are so-called simple requests and are used in the following conditions:
One of the allowed methods:
- GET
- HEAD
- POST
Apart from the headers automatically set by the user agent (for example, Connection, User-Agent, etc.), the only headers which are allowed to be manually set are the following:
Accept
Accept-Language
Content-Language
Content-Type (but note the additional requirements below)
DPR
Downlink
Save-Data
Viewport-Width
Width
The only allowed values for the Content-Type header are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
No event listeners are registered on any XMLHttpRequestUpload object used in the request; these are accessed using the XMLHttpRequest.upload property.
No ReadableStream object is used in the request.
Such requests are sent directly and the server simply successfully processes the request or replies with an error in case it didn't match the CORS rules. In any case, the response will contain the CORS headers Access-Control-Allow-*.
Pre-flighted requests
Browsers are sending the pre-flight requests if the actual request doesn't meet the simple request conditions, the most usually:
custom content types like application/xml or application/json, etc., are used
the request method is other than GET, HEAD or POST
the POST method is of an another content type than application/x-www-form-urlencoded, multipart/form-data or text/plain
You need to make sure that the response to the pre-flight request has the following attributes:
successful HTTP status code, i.e. 200 OK
header Access-Control-Allow-Origin: * (a wildcard * allows a request from any domain, you can use any specific domain to restrict the access here of course)
From the other side, the server may refuse the CORS request simply by sending a response to the pre-flight request with the following attributes:
non-success HTTP code (i.e. other than 2XX)
success HTTP code (e.g. 200 OK), but without any CORS header (i.e. Access-Control-Allow-*)
See the documentation on Mozilla Developer Network or for example HTML5Rocks' CORS tutorial for details.
I ran into a very similar problem writing an Angular 2 app - that uses a NODE server for the API.
Since I am developing on my local machine, I kept getting Cross Origin Header problems, when I would try to POST to the API from my Angular app.
Setting the Headers (in the node server) as below worked for GET requests, but my PUT requests kept posting empty objects to the database.
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT');
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type,
Origin, Authorization, Accept, Client-Security-Token, Accept-
Encoding, X-Auth-Token, content-type');
After reading Dawid Ferenczy's post, I realized that the PREFLIGHT request was sending blank data to my server, and that's why my DB entries were empty, so I added this line in the NODE JS server:
if (req.method == "OPTIONS")
{
res.writeHead(200, {"Content-Type": "application/json"});
res.end();
}
So now my server ignores the PREFLIGHT request, (and returns status 200, to let the browser know everything is groovy...) and that way, the real request can go through and I get real data posted to my DB!
Just put
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("HTTP/1.1 200 ");
exit;}
at the beginning of your serverside app and you should be fine.
For spring boot application, to enable cors request, use #CrossOrigin(origins = "*", maxAge = 3600) on your respective controller.
Refer this doc
The best is to :
have proxy.conf.json set:
{
"/api": {
"target": "http://localhost:8080",
"secure": false,
"logLevel": "debug",
"changeOrigin": true
}
}
And then to make sure that URL that you are using in angular to send a request is relative (/api/something) and not absolute (localhost:8080/api/something). Because in that case the proxy won't work.
From Chrome v79+, OPTIONS Check(pre-flight request) will no longer appear in the network tab-Source
I am building an application in Unity and exporting to HTML5. When I try to make a simple GET request I am getting this error:
Access to XMLHttpRequest at 'http://example.co.uk/request.php?foo=bar'
from origin 'http://anothersite.co.uk' has been blocked by CORS
policy: No 'Access-Control-Allow-Origin' header is present on the
requested resource.
I have seen many questions asking this on forums, and people give these sort of responses (as I've interpreted them):
The server / site you are requesting from should set the Headers, to either accept the specific domain you are requesting from or just set it as a wild card
Disable web security in your browser
Use a proxy, such as https://cors-anywhere.herokuapp.com
The first one makes sense to me, but I have tried the following on the server side to no avail:
Set the headers in the .htaccess file. Such as Header Set Access-Control-Allow-Origin "*"
Set the headers in the "request" php file: header('Access-Control-Allow-Origin: http://anothersite.co.uk'); (I also tried "*")
I tried sending the headers in the request FROM "anothersite.co.uk" in the Unity code.
I tried what people suggested with https://cors-anywhere.herokuapp.com by passing it the URL but this didn't work atall, maybe I missunderstood it. I really don't like this as a solution anyway.. makes the whole thing seem pointless.
Any tips would really help, I'm so sorry if I have just missunderstood the whole thing, but I can't make head or tail.. I just keep getting the same error shown above.
Thanks in advance for any help! :)
I use a version of the following PHP code for serving data and it seems to work.
if (isset($_SERVER['HTTP_ORIGIN'])) {
// Decide if the origin in $_SERVER['HTTP_ORIGIN'] is one
// you want to allow, and if so:
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // cache for 1 day
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
// may also be using PUT, PATCH, HEAD etc
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
exit(0);
}
I'm developing an ionic project and I'm using header parameters in each POST and GET Request. How ever When I test the project on Android Phone and monitor all requests that come into my server through my android device there are no issues. But when I deploying my ionic project and testing it in my web browser ( Chrome Web Browser ) I see that each request has been executed twice,( one without headers params and without inputs when I use POST method, and the second one is with all params ).
I've solved it in my server if there are no header parameters to ignore the request each time. How can I prevent the duplicated execution for the $http (POST and GET)?
These parameters I've set in the angular.config js file.
$httpProvider.defaults.headers.common['Accept'] = 'application/json; q=0.01';
$httpProvider.defaults.headers.common['Authorization-Token'] = value;
and my PHP service starts with
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Content-Type, Authorization-Token");
header('Access-Control-Max-Age: 60');
header('Access-Control-Allow-Methods: ["GET","POST"]');
header("Content-Type: application/json; charset=UTF-8");
Sounds like an OPTION call indeed.
It should be done, and not carry any payload, it is just to check with the server what actions are allowed on the resource before performing the actual call (post/get/whatever).
Check the answer to this similar question : Angular 2 HTTP POST does an OPTIONS call
The first request is the preflight.
This is part of the browser mechanism.
You cannot avoid it.
It all comes down to how browsers manage CORS. When making a cross-domain request in JavaScript that is not "simple" (i.e. a GET request), the browser will automatically make a HTTP OPTIONS request to the specified URL/URI, called a "pre-flight" request or "promise". As long as the remote source returns a HTTP status code of 200 and relevant details about what it will accept in the response headers, then the browser will go ahead with the original JavaScript call
Please look here and here