Cloudflare Access CORS issue

Hi everyone,

my setup is as follows:

SPA on spa.staging.foobar.tld
API on api.staging.foobar.tld

Both domains are protected via Cloudflare Access. Anyone requesting *.staging.foobar.tld needs to sign in via Google to get access.

The SPA requests the API via fetch(“api.staging.foobar.tld”, { method: “POST”, credentials: “include” }). As per https://developers.cloudflare.com/cloudflare-one/policies/zero-trust/cors the Cloudflare Access application is configured to allow credentials, allow all headers, allow all methods, and allow origins “spa.staging.foobar.tld” and “null”.

Visiting spa.staging.foobar.tld will first require Cloudflare authentication, leaving a CF_Authorization cookie for spa.staging.foobar.tld and that works fine.

Visiting spa.staging.foobar.tld also will result in the POST requests mentioned above, for which I’m seeing an OPTIONS preflight with a 200 OK response and the correct access-control-* headers (so far so good).

However, this is then followed by the actual POST request, which results in a 302 redirect to GET https://foobar.cloudflareaccess.com/cdn-cgi/access/login/api.staging.foobar.tld?kid=… which returns the Cloudflare log-in as HTML instead of the API response (in JSON), which naturally breaks my SPA.

Interestingly, visiting api.staging.foobar.tld once (by opening via the browser) stores the CF_Authorization cookie for that domain (api.staging.foobar.tld), and subsequently my SPA will work as long as that cookie is valid.

Looking at https://developers.cloudflare.com/cloudflare-one/policies/zero-trust/cors it seems to suggest that my setup “should just work”. What am I doing wrong?

Thanks in advance
Morris

Hi Morris4,

Did you manage to make it work? I have exactly the same problem, the scenario is just like you descrived and the result is the same. It would help me a lot if you did solve it.

Thank you in advance!

Pau

I just ran into this same problem. In my case, I used an Access Policy to restrict /wp-admin on a WordPress site by only allowing specific users, and it worked just fine. But when I extended that same logic to /wp-json to restrict the WordPress REST API using a second policy, this CORS issue broke everything.

What I discovered (which you already know if you made it here) was that a Bypass policy which only works with IP based access lists did not break anything, but when I wanted to use an Access policy rule with user based authentication, boom. The behavior is because the Access rule redirects users through the myhostname.cloudflareaccess.com domain, and when the browser reaches that with the origin of https://www.myhostname.com, it fails on CORS preflight for JSON POSTS with the default CORS settings.

The fix is to have the magic mix of CORS settings in your Access policy that actually works (most do not), and EVERY other piece of documentation I could find online either missed this point (assuming that it just works), or includes misinformation (such as suggesting that you add “null” to the list of allowed origins). This had me so frustrated yesterday that I felt I needed to find the most relevant thread and post an answer to it for future victims of this trap.

So, here’s the recipe for the special sauce that worked for me:

Access-Control-Allow-Credentials : Checked (this MUST be checked)
Access-Control-Max-Age (seconds) : insert number here; I used 86400 (this MUST not be blank)
Access-Control-Allow-Origin - https://www.mydomain.com (or whatever your site ORIGIN is at)
Access-Control-Allow-Methods - Allow all methods - Checked
Access-Control-Allow-Headers - Allow all http headers - Checked

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.