Cloudflare Zero Trust Site Protection

Integrating Cloudflare Zero Trust provides granular control over who can access your website or applications. Through its authentication and authorization features, you can ensure only users that meet your defined security criteria are granted access, which reduces the risk of unauthorized access and potential threats.

Prerequisites

To follow this guide you will need the following

If you don’t already have a site to use, create an Edge Delivery Website by following our developer tutorial.

Create a Cloudflare Site

Follow the steps to set up a Cloudflare site and worker using the wrangler CLI. If you’re hosting the application on a subdomain, ensure your CNAME record is updated accordingly. In this guide, we configured a CNAME record for our example application at zero-trust.example.com.

Create a site secret

Create a site access token, this token can be used to restrict access to your edge delivery site.

curl -X POST https://admin.hlx.page/config/aemsites/sites/zero-trust-site/secrets.json \
  -H 'x-auth-token: <your-auth-token>'

{
  "id": "MEXwhn7J7m1c29ngZqriA5N9DVIb67R_9394vsJ",
  "Type": "hashed",
  "value": "hlx_sQP7218fBcODiUi7NLVUH6VVT",
  "created": "2024-08-21T18:28:54.075Z",
  "lastModified": "2025-03-25T12:44:13.235Z"
}

In the response you will get back an id and a value. Keep track of these as you will need them in the next steps.

Enable site authentication using the site token

Use the token id from the response above in place of the TOKEN_ID in the body.

curl --request POST \
  --url https://admin.hlx.page/config/aemsites/sites/zero-trust-site/access/site.json \
  --header 'Content-Type: application/json' \
  --header 'x-auth-token: <your-auth-token>' \
  --data '{
    "allow": ["*@acme.com"],
    "secretId": ["MEXwhn7J7m1c29ngZqriA5N9DVIb67R_9394vsJ"]
}'

The .page and .live origins will now be protected. Users wanting to access the site directly via these origins will now need to sign into the sidekick.

Set Zero Trust Authentication methods

Navigate to the Zero Trust home from the left navigation bar in Cloudflare

and select Settings and the pick Authentication

From Login methods select the Add new button

This is your opportunity to configure the identity provider you want to use for your site. For this demo we will use One-time PIN.

Setup the Zero Trust Policies

Select Access → Policies

Select the Add a policy button

Add a policy with the name TestApp_EmailAccessPolicy and duration of 24 hours. Change either of these values as you see fit.

Under rules, you can pick whether there is an explicit list of emails you want to have access to the application or want to allow an entire domain access. For this example we will allow anyone with an email address ending in adobe.com to access the site.

Select Save

Create the Zero Trust Application

Select Access → Applications and click the Add an application button

Select Self-hosted

Enter TestApp or any name of your choice for the application name. You can keep the session duration set to 24 hours.

Select Add public hostname and enter in the domain (and optional subdomain) you setup in the first step of this guide. For path enter *. For our demo we are setting out public hostname to zero-trust.example.com

Under Access policies click Select existing policies

Select our TestApp_EmailAccessPolicy we previously created and click Confirm.

Select Next at the bottom to get to the Login methods page

Below, you’ll find a list of all the login methods permitted for our application. By default, Accept all available identity providers is selected. However, if you deselect this option, you can choose a specific login method from the list of configured options. Currently, only the One-time PIN has been set up, so it is the only available choice.

Select Next again to get to the advanced settings page.

Open the Cross-Origin Resource Sharing (CORS) settings and enable Bypass options requests to origin to let Edge Delivery handle CORS.

Select Save

Now select the 3 dots on the right of the new application and click Edit.

Take note of the Application Audience Tag, we will need this in a future step.

Update the worker

Next, we’ll update our worker to validate incoming requests, ensuring only approved traffic can access your site.

Install the jose package

In the worker code we created at the start of the guide, install the jose package. This library is designed to simplify working with JWT tokens.

npm install jose

Edit worker code

Copy the content of this file and paste it into src/index.js

Import functions from the jose package

At the top of the index file, add the following to import the required functions from the jose package.

import { jwtVerify, createRemoteJWKSet } from "jose";

Add token validation logic

Around line 26 at the top of your handleRequest method, insert the following logic to validate the JWT token provided in the cf-access-jwt-assertion header. This snippet retrieves the token, sets up the verification context by referencing your Cloudflare Access domain and audience, and uses a remote JSON Web Key Set (JWKS) to verify the token’s integrity. If the token is missing or fails verification, the worker immediately returns an error response, ensuring that only authenticated and authorized requests proceed.

try {
  const TEAM_DOMAIN = `https://${env.TEAM_DOMAIN}`;
  const AUD = env.POLICY_AUD;
  const CERTS_URL = `${TEAM_DOMAIN}/cdn-cgi/access/certs`;
  const JWKS = createRemoteJWKSet(new URL(CERTS_URL));

  const token = request.headers.get("cf-access-jwt-assertion");
  if (!token) {
    return new Response('missing required cf authorization token', { status: 403 });
  }

  await jwtVerify(token, JWKS, {
    issuer: TEAM_DOMAIN,
    audience: AUD,
  });
} catch (error) {
  return new Response(`Token verification failed: ${error.message}`, {status: 401});
}

Update wrangler.toml

Note: Some of the values below are considered sensitive and must be kept confidential; ensure they are securely stored and never committed to a public repository in GitHub.

Update route to to match your DNS setup (ex zero-trust.example.com/*)

Ensure you have the correct account_id set. To find your account_id visit the Websites Dashboard in Cloudflare, select your site and it will be listed on the right hand side of the dashboard under API.

Ensure the compatibility_date is set to at least 2025-03-17

Update the ORIGIN_HOSTNAME to the edge delivery origin host name (for instance main--zero-trust-site--aemsites.aem.live)

If commented out, remove the # in front of PUSH_INVALIDATION

Update the ORIGIN_AUTHENTICATION to the value from the site token created in a previous step (ex hlx_sQP7218fBcODiUi7NLVUH6VVTpucnHIA51yEuDFS0GE0)

Get your team domain by going to the Zero Trust dashboard and opening Settings → Custom Pages. Create a new variable with the name TEAM_DOMAIN and the your team domain (for instance dylandepass.cloudflareaccess.com)

Create a new variable called POLICY_AUD and set it to the AUD value from the Zero Trust application you created above.

Your wrangler.toml should look something like this.

name = "zero-trust-worker"

main = "src/index.mjs"
route = "zero-trust.example.com/*"
account_id = "abb72b21b09d6ac32460ac4654da1248"

compatibility_date = "2025-03-17"

[build]
command = "npm install"

[vars]
# TODO: set origin host name
ORIGIN_HOSTNAME = "main--zero-trust-site--aemsites.aem.live"

# Optional, but recommended: enable push invalidation
# see https://www.aem.live/docs/setup-byo-cdn-push-invalidation#cloudflare
PUSH_INVALIDATION = "enabled"

# Optional: enable origin authentication
# see https://www.aem.live/docs/authentication-setup-site
ORIGIN_AUTHENTICATION = "hlx_sQP7218fBcODiUi7NLVUH6VVTpucnHIA51yEuDFS0GE0"

TEAM_DOMAIN = "example-domain.cloudflareaccess.com"

POLICY_AUD = "94k128458a52641b9a5294d2cebc5kjk124021964fa01e4aal8b5c11d34948e8s0"

Congratulations, your site should now be protected by Cloudflare Zero Trust. Try navigating to your site and authenticating using a PIN. Upon successful authentication you should see your site.