I have an http request where I want some response headers that are, for one reason or another, unavailable to me from the response object but visible in the chrome dev tools
But the response object from the javascript request api only contains some of these headers.
{ // response.headers
cache-control: "no-cache, private",
content-type: "application/json"
}
Surely, there must be a way to get things like x-ratelimit-limit as part of the response. If it's information sent to the browser, why can't it be available in a javascript http request api?
This looks like a CORS request. If so, response headers are restricted unless the right Access-Control-Allow-Headers header is set. This is for security reasons.
I try to post a slack message via the fetch API in a browser:
fetch('https://hooks.slack.com/services/xxx/xxx/xx', {
method: 'post',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-type': 'application/json'
},
body: JSON.stringify({text: 'Hi there'})
})
.then(response => console.log)
.catch(error => console.error);
};
I get the following error message:
Fetch API cannot load:
https://hooks.slack.com/services/xxxxxxx/xxxxx.
Request header field Content-type is not allowed by Access-Control-Allow-Headers in preflight response.
What to do?
That Slack API endpoint unfortunately appears to be broken in its handling of cross-origin requests from frontend JavaScript code—in that it doesn’t handle the CORS preflight OPTIONS request as it should—so the only solution seems to be to omit the Content-Type header.
So it looks like you need to remove the following from the headers part of your request code:
'Content-type': 'application/json'
That part triggers your browser to do a CORS preflight OPTIONS request. So, for your browser to allow your frontend JavaScript code to send the POST request you’re trying to do, the https://hooks.slack.com/services API endpoint must return an Access-Control-Allow-Headers response header that contains Content-Type in its value.
But that endpoint doesn’t return that, so the preflight fails and the browser stops right there.
Normally when posting from frontend JavaScript to an API endpoint that expects JSON, adding that Content-Type: application/json header to the request is exactly what you need to do and should do. But not in this case—because that API endpoint doesn’t handle it properly.
What worked for me is setting Content-Type header to application/x-www-form-urlencoded.
I am using axios, and I found this suggestion in this Github thread.
It appears that this triggers a "simple request" and therefore avoids triggering CORS preflight: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests
logout(){
var destroySession='{"token":"'+this.token+'"}'
console.log("Session Destroy"+destroySession)
axios.post(eventBus.apiURL+'logout',{
headers: {
'Content-type': 'application/json',
},
body: destroySession,
}).then(response=>{
console.log("RESadas :: "+JSON.stringify(response.data))
alert("Logout successfully..!")
this.$router.push('/')
},error=>{
console.log(error);
alert("Some Issue for LogOut at Server Side..!")
});
window.localStorage.removeItem('token')
window.localStorage.removeItem('name')
this.$router.push('/')
}
<button #click="logout">Logout</button>
I have written a code for logout in vuejs, but at the time of click it sends two POST request and during session destroy problem occurs at server side.
As per Mozilla Developer Network,
Preflight Requests
Unlike “simple requests” (discussed above), "preflighted" requests
first send an HTTP request by the OPTIONS method to the resource on
the other domain, in order to determine whether the actual request is
safe to send. Cross-site requests are preflighted like this since they
may have implications to user data.
In particular, a request is preflighted if any of the following
conditions is true:
If the request uses any of the following methods:
Put
DELETE
CONNECT
OPTIONS
TRACE
PATCH
Or if, apart from the headers set automatically by
the user agent (for example, Connection, User-Agent, or any of the
other header with a name defined in the Fetch spec as a “forbidden
header name”), the request includes any headers other than those which
the Fetch spec defines as being a “CORS-safelisted request-header”,
which are the following:
Accept
Accept-Language
Content-Language
Content-Type (but note the additional requirements below)
DPR
Downlink
Save-Data
Viewport-Width
Width
Or if the Content-Type header has a
value other than the following:
application/x-www-form-urlencoded
multipart/form-data
text/plain
Updated
Please read this answer on how to disable preflight requests: Link
I have a issue by adding some HTTP header key value to a http.post. It should be very simple but I am not getting correctly set. We are using on server side a spring-boot and on client side angular framework.
Our backend request the following header values:
'Content-Type' = 'application/json'
'X-Requested-With' = 'XMLHttpRequest'
'Cache-Control' = 'no-cache'
In angular, I create a header, this header I add a RequestOptions and add this options to the post request.
See code below:
.....
let myHead = new Headers();
myHead.append('Content-Type', 'application/json');
myHead.append('X-Requested-With', 'XMLHttpRequest');
myHead.append('Cache-Control', 'no-cache');
const options = new RequestOptions({ headers: myHead });
return this.http.post(ServerUrl,data, options).map((response: Response) => {..//do Something...}
...
The problem is, if I am checking the post request, I see that the header is not correctly set.
As you can see above it is not correctly set as a key value as I am expecting!?
If I do this with a tool e.g. postman plugin you see how it should be done corretly:
POST /ias-vwa/api/auth/login HTTP/1.1
Host: de00-fm26-l1:9090
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Cache-Control: no-cache
Postman-Token: c9bc0404-bc1e-57eb-fca4-07bba9ee6d93
I tried a lot of different options to set the header but I was always ending up that it will be always set in one line like this:
Access-Control-Request-Headers:access-control-allow-origin,cache-control,content-type,x-requested-with
The error I get on the browser:
XMLHttpRequest cannot load http://t00:9090/ias-vwa/api/auth/login. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had HTTP status code 401.
Can any body give me a hint on that?
You are trying to POST to different domain from yours (your app live), and you're running on CORS issue.
You need to add Access-Control-Allow-Origin as a header in your response.
Access-Control-Allow-Origin: *
Okay I solved this issue.
As mention in this article and from Yordan Nikolov it's a CORS issue
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
If in some circumstances (as described in the article) the browser will send a Preflight-Request. In this request the custom value will be only named as in Access-Control-Request-Headers. This request will be HTTP request by the OPTIONS method. The server in our case a spring-boot need to implement a filter to filter this request and evaluate this cutsom header to be allowed. After the preflight request has be responed with a HTTP 200 the client will send the actually request with including the custom headers. This CORS issues is important if a request comes from a other domain.
So, the setting of the client side (angular2) will be set correctly.
Hope it helps anybody
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