CORS pre-flight comes back with Access-Control-Allow-Origin:*, browser still fails request
Asked Answered
S

5

26

Triggering an AJAX GET to http://qualifiedlocalhost:8888/resource.json kicks off the expected CORS pre-flight, which looks like it comes back correctly:

Pre-flight OPTIONS request

Request URL:http://qualifiedlocalhost:8888/resource.json
Request Method:OPTIONS
Status Code:200 OK

Request Headers

Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:accept, origin, x-requested-with
Access-Control-Request-Method:GET
Cache-Control:no-cache
Connection:keep-alive
Host:qualifiedlocalhost:8888
Origin:http://localhost:9000
Pragma:no-cache
Referer:http://localhost:9000/
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36

Response Headers

Access-Control-Allow-Headers:Content-Type, X-Requested-With
Access-Control-Allow-Methods:GET,PUT,POST,DELETE
Access-Control-Allow-Origin:*
Connection:keep-alive
Content-Length:2
Content-Type:text/plain
Date:Thu, 01 Aug 2013 19:57:43 GMT
Set-Cookie:connect.sid=s%3AEpPytDm3Dk3H9V4J9y6_y-Nq.Rs572s475TpGhCP%2FK%2B2maKV6zYD%2FUg425zPDKHwoQ6s; Path=/; HttpOnly
X-Powered-By:Express

Looking good?

So it should work, right?

But the subsequent request still fails with the error XMLHttpRequest cannot load http://qualifiedlocalhost:8888/resource.json. Origin http://localhost:9000 is not allowed by Access-Control-Allow-Origin.

True request

Request URL:http://qualifiedlocalhost:8888/resource.json

Request Headers

Accept:application/json, text/plain, */*
Cache-Control:no-cache
Origin:http://localhost:9000
Pragma:no-cache
Referer:http://localhost:9000/
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36
X-Requested-With:XMLHttpRequest

Help!

Maybe it's staring right in front of me. But, any ideas? Just in case it's relevant... I'm using an AngularJS $resource and talking to a CompoundJS server.

Synonymous answered 1/8, 2013 at 20:10 Comment(8)
See #16210215Sachiko
Thanks. My URLs have escaped colons. These request/response snapshots are from Chrome dev tools so they are at the browser level.Synonymous
You've left out the response headers for the actual GET request. What do those look like?Shuddering
Chrome blocks the request from ever sending, so there is no response at all.Synonymous
If Chrome blocks the request from sending, then your pre-flight is NOT succeeding. So, it seems as if you are leaving out some important information here. Also, your preflight and actual GET requests don't seem to match up (different request domains & resources).Shuddering
You're right, they should match up. I did a find/replace to make my question more generic and missed some sorry. Any idea what information I might be leaving out? The pre-flight OPTIONS request comes back with a HTTP 200 and Access-Control-Allow-Origin:*. As far as I know that should be all I need... I'll keep digging.Synonymous
I'm certain this is a bug in the browser. In my case, the server consistently sends the same CORS header, and the browser just fails on some of them. As I described in detail in my response below, there is something else in the response (possibly a cookie being set by the server, that is exposing the browser bug. I call it a bug because there is nothing in the CORS spec that says the server must not set cookies for CORS clients.Enfeoff
I am facing a similar issue, only thing is my Back-end is in SPRING, My CORS spec looks fine as pre-flight gets a pass Just want to confirm, was this issue solved?? If yes, can you please redirect me to correct answer.Weaken
S
15

change your Access-Control-Allow-Methods: 'GET, POST' to 'GET, POST, PUT, DELETE'

before:

   app.use(function(req, res, next) {
        res.setHeader('Access-Control-Allow-Origin', '*');
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
        res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type, Authorization');
        next();
    });

After:

app.use(function(req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT ,DELETE');
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type, Authorization');
    next();
});
Saturnian answered 10/6, 2016 at 6:38 Comment(0)
S
14

I recently had the same issue. The problem is that the Access-Control-Allow-Origin header (and, if you use it, the Access-Control-Allow-Credentials header) must be sent in both the preflight response and the actual response.

Your example has it only in the preflight response.

Salleysalli answered 8/12, 2014 at 20:24 Comment(1)
In this case the actual request would still hit the server and damage is done. What’s the point for the browser to look for the correct headers and prevent results being shown when the request had already been processed by the server?Bondstone
D
6

Is your web server/get function ALSO including the HTTP header: Access-Control-Allow-Origin? I eventually found success with that addition, using AngularJS 1.0.7 and a remote Java servlet. Here is my Java code snippet--no changes were required in AngularJS client:

Servlet:

@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // Send Response
    super.doOptions(request,  response);
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Requested-With");
}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    /* ... */
    response.setHeader("Access-Control-Allow-Origin", "*");
}

A more elegant alternative is a servlet filter.

Disputation answered 24/8, 2013 at 19:39 Comment(2)
Yes, in all the above scenarios, the server is sending the correct CORS headers.Enfeoff
This is the only way it works for me. resp.setHeader("Access-Control-Allow-Origin", "*");Pierre
E
4

We have been noticing the same issue, where the server is sending the correct CORS headers, but the browser fails because it thinks the CORS requirements are not being met. More interestingly, in our case, it only happens on some AJAX calls in the same browser session, but not all.

Working theory...

Clearing the browser cache solves the problem in our case -- My current working theory is that this has something to do with cookies being set by the cross origin server. I noticed in your scenario, there is a cookie being set as part of the response to the pre-flight OPTIONS request. Have you tried making that server not set any cookies for requests coming from a different origin?

However, we've noticed that in some cases, the problem re-appeared after resetting the browser. Also, running the browser in in-private mode causes the problem to go away, pointing to some problem to do with what's in the browser cache.

For reference, here is my scenario (I almost posted this as a new question in SO, but putting it here instead):

We ran into a bug where some CORS GET requests made via jQuery.ajax were failing. In a given browser session, we see the pre-flight OPTIONS request go through, followed by the actual request. In the same session, some requests go through and others fail.

The sequence of requests and responses in the browser network console look like below:

First the OPTIONS pre-flight request

OPTIONS /api/v1/users/337/statuses
HTTP/1.1 Host: api.obfuscatedserver.com 
Connection: keep-alive 
Access-Control-Request-Method: GET 
Origin: http://10.10.8.84:3003 
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36
Access-Control-Request-Headers: accept, origin, x-csrf-token, auth 
Accept: */* Referer: http://10.10.8.84:3003/ 
Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8

Which gets a response like,

HTTP/1.1 200 OK 
Date: Tue, 06 Aug 2013 19:18:22 GMT 
Server: Apache/2.2.22 (Ubuntu) 
Access-Control-Allow-Origin: http://10.10.8.84:3003 
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT 
Access-Control-Max-Age: 1728000 
Access-Control-Allow-Credentials: true 
Access-Control-Allow-Headers: accept, origin, x-csrf-token, auth 
X-UA-Compatible: IE=Edge,chrome=1 
Cache-Control: no-cache 
X-Request-Id: 4429c4ea9ce12b6dcf364ac7f159c13c 
X-Runtime: 0.001344 
X-Rack-Cache: invalidate, pass 
X-Powered-By: Phusion 
Passenger 4.0.2 
Status: 200 OK 
Vary: Accept-Encoding
Content-Encoding: gzip

Then the actual GET request,

GET https://api.obfuscatedserver.com/api/v1/users/337
HTTP/1.1 
Accept: application/json, text/javascript, */*; q=0.01 
Referer: http://10.10.8.84:3003/ 
Origin: http://10.10.8.84:3003 
X-CSRF-Token: xxxxxxx 
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36 
auth: xxxxxxxxx

Which gets an error in the browser console like,

XMLHttpRequest
  cannot load https://api.obfuscatedserver.com/api/v1/users/337.
  Origin http://10.10.8.84:3003 is not allowed by Access-Control-Allow-Origin.

Additional Debugging

I can replay the same sequence via curl, and I see valid responses coming from the server. i.e. they have the expected CORS headers that should let the requests go through.

Running the same scenario in an in-private / incognito browser window did not reproduce the problem. This lead me to try clearing the cache, and that made the problem go away too. But after a while, it came back.

The issue was reproducing on iPhone safari as well as on Chrome on OSX desktop.

What I really need help with

I'm suspecting there are some cookies set in the cross-origin domain that are getting involved here and potentially exposing a bug in the browser. Are there tools or breakpoints I can set in the browser (native stack?) to try and debug this further? A breakpoint in the code that evaluates the CORS policy would be ideal.

Enfeoff answered 8/8, 2013 at 0:5 Comment(7)
Does anyone have tips on how to break into chrome and see what it is doing when the CORS request fails?Enfeoff
Did anyone find out further information on this? I am having the same issue, and am unable to resolve it.Neurasthenia
@Neurasthenia We could never get it to work, so ended up implementing a proxy on the origin server to forward the appropriate requests to the x-domain server. In our case, these were all XHR calls going to our API endpoint and it was easy to map these based on the incoming URL path.Enfeoff
If your browser requests a resource without CORS, or with a different origin, then later requests the same resource, the cached copy of the resource has the access-control-allow-origin from the original request. This can happen if multiple subdomains request a resource from the same subdomain. The origins are different, all allowed, but the cached resource is only valid for the first to request it.Shaduf
The Access-Control-Max-Age: 1728000 will cause the CORS configuration for this resource to be cached for a long time. It's not cookies but the CORS rule itself which is cached. So e.g. if you change the CORS requirements during dev, but the rule doesn't get refreshed, then it can fail. Clearing the browser cache then "fixes" it. Once it's all working and settled the caching helps by reducing the amount of preflight OPTIONS requests in production, but when youre developing you don't want such a long cache time.Kebab
@Kebab that sounds like the right answer. You should submit it as a new answer so that it doesn't get lost in this comment thread of my non-answer :-) Thank youEnfeoff
Oh right, I hadn't considered it as an answer. Perhaps this answers many similar questions when "CORS doesn't work like it should", just because an old config is being cached. So confusing!Kebab
N
0

Looks like your response to 'options' is working fine. The problem seems to be in the 'get' response.

You'll want to have your 'get' response return the same CORS headers as the 'options' response.

In particular, 'get' response should include

Access-Control-Allow-Headers:Content-Type, X-Requested-With
Access-Control-Allow-Methods:GET,PUT,POST,DELETE
Access-Control-Allow-Origin:*
Neoclassic answered 12/9, 2013 at 16:52 Comment(1)
Only Access-Control-Allow-Origin (and, if you use it, Access-Control-Allow-Credentials) is requiredSalleysalli

© 2022 - 2024 — McMap. All rights reserved.