Skip to content

CORS

Cross-Origin Resource Sharing (CORS) is a mechanism that uses HTTP headers to grant a web application running on one origin permission to reach selected resources in a different origin. The web application executes a cross-origin HTTP request when it requests a resource that has a different origin from its own, including domain, protocol, or port.

For a CORS request to reach a site protected by Access, the request must include a valid CF-Authorization cookie. This may require additional configuration depending on the type of request:

Allow simple requests

If you make a simple CORS request to an Access-protected domain and have not yet logged in, the request will return a CORS error. There are two ways you can resolve this error:

Authenticate manually

  1. Visit the target domain in your browser. You will see the Access login page.
  2. Log in to the target domain. This generates a CF-Authorization cookie.
  3. Refresh the page that made the CORS request. The refresh resends the request with the newly generated cookie.

Allow preflighted requests

If you make a preflighted cross-origin request to an Access-protected domain, the OPTIONS request will return a 403 error. This error occurs regardless of whether you have logged in to the domain. This is because the browser never includes cookies with OPTIONS requests, by design. Cloudflare will therefore block the preflight request, causing the CORS exchange to fail.

There are three ways you can resolve this error:

Bypass OPTIONS requests to origin

You can configure Cloudflare to send OPTIONS requests directly to your origin server. To bypass Access for OPTIONS requests:

  1. In Zero Trust, go to Access > Applications.
  2. Locate the origin that will be receiving OPTIONS requests and select Edit.
  3. In the Settings tab, scroll down to CORS settings.
  4. Turn on Bypass options requests to origin. This will remove all existing CORS settings for this application.

It is still important to enforce CORS for the Access JWT — this option should only be used if you have CORS enforcement established in your origin server.

Configure response to preflight requests

You can configure Cloudflare to respond to the OPTIONS request on your behalf. The OPTIONS request never reaches your origin. After the preflight exchange resolves, the browser will then send the main request which does include the authentication cookie (assuming you have logged into the Access-protected domain).

To configure how Cloudflare responds to preflight requests:

  1. In Zero Trust, go to Access > Applications.

  2. Locate the origin that will be receiving OPTIONS requests and select Edit.

  3. In the Settings tab, scroll down to CORS settings.

  4. Configure the dashboard CORS settings to match the response headers sent by your origin.

    For example, if you have configured api.mysite.comto return the following headers:

    headers: {
    'Access-Control-Allow-Origin': 'https://example.com',
    'Access-Control-Allow-Credentials' : true,
    'Access-Control-Allow-Methods': 'GET, OPTIONS',
    'Access-Control-Allow-Headers': 'office',
    'Content-Type': 'application/json',
    }

    then go to api.mysite.com in Access and configure Access-Control-Allow-Origin, Access-Control-Allow-Credentials, Access-Control-Allow-Methods, and Access-Control-Allow-Headers. Example CORS settings configuration in Zero Trust

  5. Select Save application.

  6. (Optional) You can check your configuration by sending an OPTIONS request to the origin with curl. For example,

    Terminal window
    curl --head --request OPTIONS https://api.mysite.com \
    --header 'origin: https://example.com' \
    --header 'access-control-request-method: GET'

    should return a response similar to:

    HTTP/2 200
    date: Tue, 24 May 2022 21:51:21 GMT
    vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
    access-control-allow-origin: https://example.com
    access-control-allow-methods: GET
    access-control-allow-credentials: true
    expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
    report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=A%2FbOOWJio%2B%2FjuJv5NC%2FE3%2Bo1zBl2UdjzJssw8gJLC4lE1lzIUPQKqJoLRTaVtFd21JK1d4g%2BnlEGNpx0mGtsR6jerNfr2H5mlQdO6u2RdOaJ6n%2F%2BS%2BF9%2Fa12UromVLcHsSA5Y%2Fj72tM%3D"}],"group":"cf-nel","max_age":604800}
    nel: {"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}
    server: cloudflare
    cf-ray: 7109408e6b84efe4-EWR

Send authentication token with Cloudflare Worker

If you have two sites protected by Cloudflare Access, example.com and api.mysite.com, requests made between the two will be subject to CORS checks. Users who log in to example.com will be issued a cookie for example.com. When the user’s browser requests api.mysite.com, Cloudflare Access looks for a cookie specific to api.mysite.com. The request will fail if the user has not already logged in to api.mysite.com.

To avoid having to log in twice, you can create a Cloudflare Worker that automatically sends authentication credentials to api.mysite.com.

Prerequisites

1. Generate a service token

Follow these instructions to generate a new Access service token. Copy the Client ID and Client Secret to a safe place, as you will use them in a later step.

2. Add a Service Auth policy

  1. In Zero Trust, go to Access > Applications.

  2. Find your api.mysite.com application and select Edit.

  3. Select the Policies tab.

  4. Add the following policy:

    ActionRule typeSelector
    Service AuthIncludeService Token

3. Create a new Worker

Open a terminal and run the following command:

Terminal window
npm create cloudflare@latest -- authentication-worker

This will prompt you to install the create-cloudflare package and lead you through setup.

For setup, select the following options:

  • For What would you like to start with?, choose Hello World example.
  • For Which template would you like to use?, choose Hello World Worker.
  • For Which language do you want to use?, choose JavaScript.
  • For Do you want to use git for version control?, choose Yes.
  • For Do you want to deploy your application?, choose No (we will be making some changes before deploying).

Go to your project directory.

Terminal window
cd authentication-worker

Open /src/index.js and delete the existing code and paste in the following example:

// The hostname where your API lives
const originalAPIHostname = "api.mysite.com";
export default {
async fetch(request) {
// Change just the host. If the request comes in on example.com/api/name, the new URL is api.mysite.com/api/name
const url = new URL(request.url);
url.hostname = originalAPIHostname;
// If your API is located on api.mysite.com/anyname (without "api/" in the path),
// remove the "api/" part of example.com/api/name
// url.pathname = url.pathname.substring(4)
// Best practice is to always use the original request to construct the new request
// to clone all the attributes. Applying the URL also requires a constructor
// since once a Request has been constructed, its URL is immutable.
const newRequest = new Request(url.toString(), request);
newRequest.headers.set("cf-access-client-id", CF_ACCESS_CLIENT_ID);
newRequest.headers.set("cf-access-client-secret", CF_ACCESS_CLIENT_SECRET);
try {
const response = await fetch(newRequest);
// Copy over the response
const modifiedResponse = new Response(response.body, response);
// Delete the set-cookie from the response so it doesn't override existing cookies
modifiedResponse.headers.delete("set-cookie");
return modifiedResponse;
} catch (e) {
return new Response(JSON.stringify({ error: e.message }), {
status: 500,
});
}
},
};

Then, deploy the Worker to your Cloudflare account:

Terminal window
npx wrangler deploy

4. Configure the Worker

  1. Log in to the Cloudflare dashboard, select your account and go to Workers & Pages.

  2. Select your newly created Worker.

  3. In the Triggers tab, go to Routes and add example.com/api/*. The Worker is placed on a subpath of example.com to avoid making a cross-origin request.

  4. In the Settings tab, select Variables.

  5. Under Environment Variables, add the following secret variables:

    • CF_ACCESS_CLIENT_ID = <service token Client ID>
    • CF_ACCESS_CLIENT_SECRET = <service token Client Secret>

The Client ID and Client Secret are copied from your service token.

  1. Enable the Encrypt option for each variable and select Save.

5. Update HTTP request URLs

Modify your example.com application to send all requests to example.com/api/ instead of api.mysite.com.

HTTP requests should now work seamlessly between two different Access-protected domains. When a user logs in to example.com, the browser makes a request to the Worker instead of to api.mysite.com. The Worker adds the Access service token to the request headers and then forwards the request to api.mysite.com. Since the service token matches a Service Auth policy, the user no longer needs to log in to api.mysite.com.

Troubleshooting

In general, we recommend the following steps when troubleshooting CORS issues:

  1. Capture a HAR file with the issue described, as well as the JS console log output recorded simultaneously. This is because the HAR file alone will not give full visibility on the reason behind cross-origin issues.
  2. Ensure that the application has set credentials: 'same-origin' in all fetch or XHR requests.
  3. If you are using the cross-origin setting on script tags, these must be set to “use-credentials”.