HTTP request from Angular sent as OPTIONS instead of POST
Solution 1
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 theapplication/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 other2XX
) response, the browser will send the actual request,POST
in your case
- If the server replies with a non-
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 theXMLHttpRequest.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
orapplication/json
, etc., are used - the request method is other than
GET
,HEAD
orPOST
- the
POST
method is of an another content type thanapplication/x-www-form-urlencoded
,multipart/form-data
ortext/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.
Solution 2
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.
Solution 3
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!
Solution 4
For spring boot application, to enable cors request, use @CrossOrigin(origins = "*", maxAge = 3600)
on your respective controller.
Solution 5
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.
Related videos on Youtube
David
Updated on July 09, 2022Comments
-
David almost 2 years
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 asPOST
. Is there a solution how to fix this strange issue?-
Nandan Bhat over 7 yearsTry adding 'Accept': 'application/json', in the header
-
Valentin Roudge over 7 yearsThat OPTIONS is a preflight request, sent by the browser because there are custom headers set in your POST call ; it's normal. I think the issue is with the content of these headers.
-
David Ferenczy Rogožan over 7 yearsThe problem is that the response to the preflight (
OPTIONS
) request is not success, i.e. HTTP 400. That why the real (following) request fails. You have to make sure the response to the preflight request is valid. -
David Ferenczy Rogožan over 7 yearsWhat do you mean by the HTTP status code
400
is expected? -
georgeawg over 7 yearsThe POST request appends a header named "foo" and that name isn't listed in Access-Control-Allow-Headers. Either add "foo" to that list or don't post with that header.
-
David Ferenczy Rogožan over 7 yearsDid you solve it? If yes, it would be good if you could describe why your webserver or application has been returning HTTP 400 for the
OPTIONS
request and how did you fix it. You can for example create another answer for that.
-
-
David Ferenczy Rogožan about 5 yearsWell, you're not ignoring the pre-flight request, you're properly replying to it. It's not a purpose of the pre-flight request to tranfer any data, it's purpose is just to check if the real request will be allowed from the current domain.
-
Mehdi Roostaeian almost 5 yearsthanks for your clear description, solved my problem.