I am trying to understand how the whole CORS policy works. To explain my confusion, let me give you an example:
$.get("https://www.google.com", function(response) { alert(response) });
The above request will return with the following error:
XMLHttpRequest cannot load https://www.google.com/. Redirect from 'https://www.google.com/' to 'https://www.google.ca/?gfe_rd=cr&ei=TlqUWeGEH5HRXqW6utgI' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://fiddle.jshell.net' is therefore not allowed access.
Now in order for that to work, google would have to white-list https://fiddle.jshell.net.
Now, if I were to try the same thing on a restful API page, that will work. My question is really simple, Why?
Trying to analyze this, I tried hitting an API and analyzing its response:
https://apigee.com/console/bing?req=%7B%22resource%22%3A%22web_search%22%2C%22params%22%3A%7B%22query%22%3A%7B%22query%22%3A%22sushi%22%2C%22sources%22%3A%22web%22%7D%2C%22template%22%3A%7B%22format%22%3A%22json%22%7D%2C%22headers%22%3A%7B%7D%2C%22body%22%3A%7B%22attachmentFormat%22%3A%22mime%22%2C%22attachmentContentDisposition%22%3A%22form-data%22%7D%7D%2C%22verb%22%3A%22get%22%7D
Response:
HTTP/1.1 200
Date:
Wed, 16 Aug 2017 14:31:32 GMT
Content-Length: 266
Connection: keep-alive
Content-Type: application/json; charset=utf-8
Server: Apigee Router
X-Content-Type-Options: nosniff
I came to conclusion that it must be the headers. Specifically I belive that it is this header: Content-Type: application/json; But I don't know for sure, I am trying to understand this and hoping somebody here can explain to me.
So I did 2 tests: running your code $.get("https://www.google.com", function(response) { alert(response) }); snippet from the console and requesting https://www.google.com from https://apigee.com/console/others
I think what happens in the 1st case is the fact that the request is done from the client, next request headers are sent:
:authority:www.google.com
:method:GET
:path:/?_=1502896196820
:scheme:https
accept:*/*
accept-encoding:gzip, deflate, br
accept-language:en-US,en;q=0.8
origin:https://stackoverflow.com
referer:https://stackoverflow.com/questions/45717044/understanding-page-response
user-agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3187.0 Safari/537.36
x-chrome-uma-enabled:1
x-client-data:CJG2yQEIo7bJAQiMmMoBCKudygEIs53KAQjRncoBCKiiygE=
Since Google does not reply with 'Access-Control-Allow-Origin: *' - client, and in the request I have origin:https://stackoverflow.com, Chrome in my case throws CORS error.
In the 2nd test, using https://apigee.com/console/others and requesting https://www.google.com , apigee.com seems to overwrite headers and sends:
GET / HTTP/1.1
Host:
www.google.com
X-Target-URI:
https://www.google.com
Connection:
Keep-Alive
Also, from DEV console, I can see it does server to server call so no client involved in throwing CORS, thus I am getting the responses with Google page.
UPDATE:
Regarding JSON API requests, here is some interesting info from Google CloudPlatform about CORS
Note: CORS configuration applies only to XML API requests. For JSON
API requests, Cloud Storage returns the Access-Control-Allow-Origin
header with the origin of the request.
Thus, if the request is performed from the client, a client should not throw CORS errors since it gets Access-Control-Allow-Origin with the same origin it sent.
However, different APIs and clients might process requests differently. Thus, sometimes Firefox throws CORS while Chrome does not.
Related
I got this code:
var req = new HttpRequest();
req.open("POST", "http://localhost:8031/rest/user/insert");
req.setRequestHeader("Content-type", "application/json");
req.send(json.stringify(user_map));
But, instead of sending the POST verb, when I see it in fiddler I see this:
OPTIONS http://localhost:8031/rest/user/insert HTTP/1.1
Host: localhost:8031
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://127.0.0.1:3030
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 (Dart) Safari/537.33
Access-Control-Request-Headers: origin, content-type
Accept: */*
Referer: http://127.0.0.1:3030/E:/grole/dart/Clases/Clases/web/out/clases.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: es-ES,es;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
As you can see, it is using the OPTIONS verb instead of POST?
What's going on?
The OPTIONS verb is a preflight request sent by some browsers to check the validity of cross origin requests. It pretty much checks with the server that the Origin(requester) is allowed to make the request for a specified resource. Also, depending on which headers are sent back by the server it lets the browser know which headers, methods, and resources the origin is allowed to request form the server.
The browser sends the OPTIONS request then if the server answers back with the correct headers (CORS headers) allowing the origin to make the request, you should see your POST request go through afterwards.
Note that the CORS headers must be returned on both the OPTIONS response as well as the POST response. This means your server must be able to respond to the options method on the routes you want to access across domains.
This is known as Cross-origin Resource Sharing. Mozilla has some pretty good documentation on the subject. https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS
If you have more questions let me know and I'll answer them.
One way to avoid this problem is by sending the request payload without custom headers and using formData to setup your request payload.
The following code (using the PouchDB Authentication plugin) fails because it triggers the browser to send a CORS preflight request, and CouchDB does not support the OPTIONS HTTP method.
var db = new PouchDB("http://localhost:5984/mydb");
db.login('username', 'password');
// assume the database URL and login info are valid
Here is the error (in Chrome). Note that this issue also occurs in Edge, but not in Firefox:
XMLHttpRequest cannot load http://localhost:5984/_session. Response for preflight has invalid HTTP status code 405
And here are the headers that Chrome is sending for the request (they are not significantly different in Firefox):
POST /_session HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 25
Accept: application/json
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,es-419;q=0.6,es;q=0.4
I have already enabled CORS via the add-cors-to-couchdb Node script. Things I have tried:
Manually adding OPTIONS as a method under [cors] in my local.ini
Passing { ajax: { content_type: "text/plain" } } as the third argument to login
So, my question is:
How can I prevent the preflight request from being triggered? Looking at the MDN documentation, it doesn't seem necessary.
If the previous is not possible, how can I set my CouchDB server up to respond to preflight requests?
Hitting this same issue. Seems Chrome has recently started being a little more aggressive about sending the OPTIONS preflight.
A partial work around was to specify a specific origin in the CORS header instead of '*',
so
curl -X PUT $HOST/_config/cors/origins -d '"localhost:8080"'
or similar.
I still am getting the preflight error, but now PouchDB successfully authenticates, so I can just ignore the error.
I think the fix is to get CouchDB to respond to OPTIONS on the _session url.
Edit, more info here
https://github.com/nolanlawson/pouchdb-authentication/issues/111
With the latest PouchDB you authenticate like so:
var remote = new PouchDB("http://user:password#localhost:4984/bucket/");
After not being able to figure out what was going wrong, I thought that I'd try my luck on here to see if anyone knows what's going on.
I have an angularJS app with a GoLang/Gorilla mux server backend.
The web app is on http://localhost:8888/ and the server, http://localhost:8080/
Basically I have this simple Javascript GET request:
$.get('http://localhost:8080/api/v1'+'/locations/', {borough: "Queens"})
Using inspect element, in the response headers I can see the following:
Content-Length:68
Content-Type:text/html; charset=utf-8
Date:Sun, 17 Apr 2016 20:12:00 GMT
Location:/api/v1/locations?borough=queens
And the following in the console:
XMLHttpRequest cannot load http://localhost:8080/api/v1/locations/? borough=queens. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8888' is therefore not allowed access.
I try the exact same request using Postman and see a 401 with:
Access-Control-Allow-Origin →*
Content-Length →0
Content-Type →text/plain; charset=utf-8
Date →Sun, 17 Apr 2016 19:46:53 GMT
Which is what I expect. For some reason, it appears that my request is not even making it to the server, which does not offer a 301 request. The fact that Postman and the app give completely different responses is perplexing.
I had some issues with CORS but thought I resolved that. The fact that Postman gives me a different result with the Access-Control-Allow-Origin header makes me think this is something else.
I welcome any help.
Edit:
I made the server reply back with the standard Cors parameters. This is what I get now from Postman:
Access-Control-Allow-Headers →Origin, X-Requested-With, Content-Type, Accept, Authorization
Access-Control-Allow-Methods →GET, POST, PUT, DELETE
Access-Control-Allow-Origin →*
Content-Length →0
Content-Type →text/plain; charset=utf-8
Date →Sun, 17 Apr 2016 20:27:31 GMT
Still get the 301 for the JS call...
Edit2: (tried setting Postman call to exact same as network request)
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Host:localhost:8080
Origin:http://localhost:8888
Referer:http://localhost:8888/root/mainapp/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36
Still get the same 301 from inspect element and expected 401 with Postman. How can it be that in a Restful HTTP context, we have two identical HTTP calls and yet have completely different behavior out of it?!
So basically when I removed the final slash to make it:
from
$.get('http://localhost:8080/api/v1'+'/locations/', {borough: "Queens"})
to
$.get('http://localhost:8080/api/v1'+'/locations', {borough: "Queens"})
This worked!!! I have no bloody idea as to why. If I would love to hear from anyone that may know what happened here and why this works. I'll reiterate- because of the last slash, the request didn't even make it to the server.
Hello so i'm trying to call my backend and getting some strange issue with calling post method.
Remote Address:192.168.58.183:80
Request URL:http://192.168.58.183/ESService/ESService.svc/CreateNewAccount
Request Method:OPTIONS
Status Code:405 Method Not Allowed
Request Headersview source
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8,pl;q=0.6
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:192.168.58.183
Origin:http://localhost:8100
Referer:http://localhost:8100/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) > AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93
Safari/537.36
Response Headersview source
Access-Control-Allow-Headers:Content-Type, Accept
Access-Control-Allow-Methods:POST,GET,OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Max-Age:1728000
Allow:POST
Content-Length:1565
Content-Type:text/html; charset=UTF-8
Date:Mon, 02 Feb 2015 09:11:17 GMT
Server:Microsoft-IIS/7.5
X-Powered-By:ASP.NET
And my code looks like here i think this call is okay can someone review it?
$scope.RegisterUser = function(){
var us = {
UserName:$scope.userName,
Password:$scope.password,
UserRoleID:null,
Company:$scope.company,
Terms:$scope.terms,
ID:null,
BuyerID:app.buyerId
};
$http({method:'POST', url:app.wcf + '/CreateNewAccount', data:{us:us}})
.then(
function(resp){
app.Logger(resp.data);
},
function(err){
app.Logger(err);
})};
So maybe i'm doing something wrong or i need to pass optional config to http?
Normally, browsers will not allow your site's JavaScript to read the data from a cross-origin request. This is because your site might be instructing the browser to get information from the user's online banking, company intranet, or some other private site. This is called The Same Origin Policy.
A standard called CORS allows a site to give permission to another site to read data from it.
Since POST requests can have side effects, an additional layer of security is added. Before the browser will make the POST request, it will make a pre-flight OPTIONS request to ask for permission to make the POST request.
Your server is not configured to handle that OPTIONS request (and probably isn't configured to return the CORS headers for the POST request either).
You need to set up CORS support if you want to allow your JavaScript to make requests to it from a different origin.
This is a pre flight request and is used to enable CORS there is no need to be concerned this is normal.
Vitals:
Chrome Version 23.0.1271.64
Mac OS X 10.8.2
The Situation:
I have a web service exposed at https://api.myapi.com/...
I would like to allow JavaScript applications to interact with this API. CORS feels like the obvious best fit (jsonp and friends being terrible, terrible hacks).
I can see in my Developer Tools that preflight requests are being sent out:
Request URL: https://api.myapi.com/someapi
Request Method: OPTIONS
Status Code: 200 OK
Request Headers
Accept: */*
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Access-Control-Request-Headers: origin, authorization, accept
Access-Control-Request-Method: GET
Connection: keep-alive
Host: api.myapi.com
Origin: http://prototypes.mycompany.com
Referer: http://prototypes.mycompany.com/somepage.html
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11
Response Headers
Access-Control-Allow-Headers: Accept,Authorization,Content-Length,Content-Type,Cookie
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS
Access-Control-Allow-Origin: http://prototypes.mycompany.com
Access-Control-Allow-Credentials: true
Connection: close
This looks great, in particular the response Access-Control-Allow-Origin header seems to match the request Origin header precisely.
Immediately afterwards, the actual request is attempted:
Request URL: https://api.myapi.com/someapi
Request Headers
Accept: application/json, text/javascript, */*; q=0.01
Authorization: <custom authentication scheme, removed>
Origin: http://prototypes.mycompany.com
Referer: http://prototypes.mycompany.com/somepage.html
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11
It then shows up as "Cancelled" and the following error is emitted:
XMLHttpRequest cannot load https://api.myapi.com/someapi. Origin http://prototypes.mycompany.com is not allowed by Access-Control-Allow-Origin.
I originally started with * as my allowed origin, but that fails with precisely the same problem.
I have replicated this problem on Mobile Safari for iOS 5, and Firefox as well. In Firefox the behavior is even more confusing:
The server responds 200 OK and then 6-7KB of data (which is what I expect). The client side log says that it got a 200 OK and ~300 bytes of data (with no response headers, no response at all...) and then triggers the "error" handler.
What am I missing?
Problem solved! I had to break out gdb to figure it out though...
A proxy server was inserting its own CORS headers into the request. So the HTTP session had the headers specified multiple times. The security check was checking against the first header, which was "*", and fails the request. The request failure prints out the last header value, which looks correct.
Not very helpful, WebKit!
Ideas:
I notice you don't have Origin in your Access-Control-Allow-Headers. I wouldn't think they'd be necessary, but you have Content-Length and I wouldn't think that'd be necessary either.
You're using Authorization, do you think you're supposed to use credentials? (Apparently sometimes you're supposed to include Access-Control-Allow-Credentials: true in the response.)
(These barely merit an "answer" but the stupid comments don't allow newlines.)