Knowledge · Cloudflare

Protect a Website and Its Previews with Cloudflare Zero Trust Free

A step-by-step guide to the free 50-user plan, Access policies, and Worker-wide or preview-only protection

Set up Cloudflare Zero Trust Free, allow specific email addresses, and protect all traffic or just Workers Previews based on an actual deployment experience.

Not every website deployed on Cloudflare Workers needs to be public. I wanted my personal investment dashboard accessible only to me, while keeping the in-progress previews for my blog private until publication.

It would be possible to build a login system from scratch. Instead, I used Cloudflare Zero Trust Access to require authentication in front of an existing Worker without substantially changing the application.

I chose different protection scopes for two services.

ServiceAccess scopeDesired behavior
Personal INVESTAll trafficRequire sign-in for production, APIs, workers.dev, and previews
Public BLOGPreviews onlyKeep the public blog open but require sign-in for previews

This guide records the dashboard workflow as I used it on October 24, 2026. Cloudflare may change menu names and plan details; consult the official documentation linked at the end if a screen looks different.

1. What does Zero Trust Free include?

At the time of writing, Cloudflare Zero Trust Free costs $0 and supports up to 50 users.

ItemZero Trust Free
Price$0
User limitUp to 50
Trial expirationNo fixed expiration for the Free plan itself
Access policies and authenticationAvailable
First-time organization setupPayment information required, even on Free

The 50-user limit is not a limit on Cloudflare accounts, domains, Workers, or monthly site visitors. It refers to user seats consumed by identities that authenticate through Access or enroll through the Cloudflare One Client. One identity accessing multiple applications normally consumes a single seat, regardless of how many times that user signs in. Seats may remain occupied until explicitly freed.

Also, Zero Trust Free is separate from Workers, D1, and other Cloudflare product pricing. Providing billing information does not itself create a charge for subscribing to Zero Trust Free, but separate paid products and features should be checked independently.

Cloudflare's official organization setup instructions require payment details even when Free is selected, and explicitly say that the Free-plan setup step does not charge you.

2. Create a Zero Trust organization

Assume that you already have a Cloudflare account and have deployed a Worker.

  1. Log in to the Cloudflare Dashboard.
  2. Select Zero Trust from the navigation menu.
  3. On the initial Cloudflare One setup screen, choose a Team name. This is an organizational identifier, not your email address or password.
  4. Select Zero Trust Free as the plan.
  5. Enter the requested billing information and finish setting up the organization.
  6. Return to Zero Trust and confirm that Access controls is available.

During my setup, Workers & Pages → invest → Access kept redirecting me to Cloudflare One onboarding. I could only proceed with Worker protection after finishing the organization's initial setup.

The onboarding item Connect a private web application may lead through a separate application connection flow. If the application is already deployed as a Cloudflare Worker, a new Tunnel is not required merely to protect the entire Worker. After activating Zero Trust, return to the Worker's Access tab.

3. Create an allow policy for your email address

First define who is allowed to sign in.

Dashboard path:

Zero Trust → Access controls → Policies → Add a policy

For my private dashboard, I used these settings:

FieldValue
Policy nameONSEMO Invest Owner Only
ActionAllow
Session duration24 hours
Include → SelectorEmails
Include → ValueMy one authorized email address
Override global MFAOff
  1. Give the policy a recognizable name and choose Allow.
  2. Under Include, select Emails.
  3. Enter the exact email address that should have access.
  4. Review session duration and MFA settings.
  5. Select Save policy.

Avoid Everyone for a private site. Email domain can authorize multiple people who share the same email domain. Emails with one exact address is the suitable choice for this owner-only case.

Creating the policy does not protect a website on its own. The next step is to apply it to the intended Worker.

Should MFA be enabled?

I left Override global MFA at Off. This does not mean that Cloudflare Access is instructed to disable all MFA: it leaves the higher-level organization or application settings in effect.

Cloudflare distinguishes between MFA performed by an identity provider and Independent MFA enforced by Access itself. A private application containing important personal records should consider adding a stronger MFA requirement.

To review application-level MFA:

Zero Trust → Access controls → Applications → select the application → Configure → Authentication → MFA

Policy-level MFA can also be reviewed under Policies → select the policy → Configure → Multi-factor authentication. A policy's MFA configuration may override application or organization defaults.

4. Protect all traffic to the personal INVEST Worker

A critical concern was avoiding an incomplete setup that protects only the custom domain while leaving an alternative workers.dev entry point public.

Dashboard path:

Cloudflare Dashboard → Workers & Pages → invest → Access

Follow these steps:

  1. Select the invest Worker.
  2. Open the Access tab.
  3. Click Protect this Worker behind Access.
  4. Choose All traffic.
  5. Under Authentication policy, select ONSEMO Invest Owner Only.
  6. Check the selected Worker and scope, then click Apply Access.

The important choice is Protect this Worker, not Protect all Workers. The latter can put other Workers in the same account, including public websites, behind authentication.

Per Cloudflare's documentation, All traffic at the Worker level covers associated:

  • Custom Domains such as invest.onsemo.com
  • Worker Routes
  • workers.dev hostnames
  • Worker Preview URLs
  • Application files and API paths reached through those routes

This differs from hostname-based Access, which protects only the configured hostname or path.

One technical limitation: Worker-level Access does not currently support WebSocket upgrades. These requests can return 403. Applications that rely on WebSockets may need a hostname-based Access configuration instead.

5. Protect BLOG previews while keeping production public

BLOG should remain readable by everyone. Only unpublished previews should require authentication, so do not choose All traffic for a public blog.

Dashboard path:

Cloudflare Dashboard → Workers & Pages → blog → Access

  1. Choose the blog Worker.
  2. Select Access → Protect this Worker behind Access.
  3. Select Previews only.
  4. Select the owner-only Authentication policy.
  5. Select Apply Access.

This keeps https://blog.onsemo.com public while requiring Access authentication for the Worker's preview deployments.

A reusable Access policy can be shared across different applications. I reused the owner-only policy created for INVEST. Be aware that future edits to that policy can affect every application using it. For a shared policy, a generic name such as ONSEMO Owner Only is easier to maintain.

6. Hostname-based Access versus Worker-level Access

Initially, I also looked at the Zero Trust application creation screen:

Zero Trust → Access controls → Applications → Create new application

A Self-hosted application can protect a specific public hostname or path. It is a valid option, but it has a different scope.

ApproachWhat it protectsTypical use
Self-hosted Public hostnameA specified hostname or URL pathRestrict one domain or endpoint
Worker All trafficProduction and previews for one WorkerMake an entire personal Worker private
Worker Previews onlyPreviews for one WorkerKeep production public and previews private

Because both of my sites already ran on Cloudflare Workers, I used the Worker's Access tab rather than registering hostnames one by one.

If several Access rules match one request, hostname/path policies take precedence over Worker-level policies, followed by account-wide Worker policies. Check for existing exceptions when combining different rule types.

7. Verify the result without an existing login session

An Access configuration is not complete just because its settings were saved. A browser that is already signed in may hide problems. Use incognito mode or another signed-out browser.

Verify INVEST

  1. Open https://invest.onsemo.com in incognito mode.
  2. Confirm that the Cloudflare Access login page appears.
  3. Sign in with the authorized account and check that the dashboard works.
  4. Verify that an unauthorized account cannot access it.
  5. Test the workers.dev hostname and important API paths without authentication.
  6. If previews exist, test their URLs as well.

Unauthenticated access may be redirected to a login page or return an access-denied status. The important outcome is that application data and API responses are not available without authorization.

After enabling INVEST Access, I also ran GitHub Actions checks from an unauthenticated environment against key app and API routes.

Verify BLOG

  1. Open https://blog.onsemo.com in a signed-out browser. The public site should load without a login.
  2. Open an actual Cloudflare Preview URL from a GitHub pull request. An Access login page should appear.
  3. Sign in and confirm that the preview renders correctly.

A GitHub branch is not itself a preview website. Its deployed Cloudflare Preview URL must be checked separately. A private GitHub repository also does not automatically make its preview deployments private.

8. Useful administration menu paths

TaskCloudflare dashboard
Change allowed email addressesZero Trust → Access controls → Policies
Review Access applicationsZero Trust → Access controls → Applications
Change Worker protection scopeWorkers & Pages → select Worker → Access
Adjust application MFAAccess controls → Applications → application → Authentication → MFA
Review user seatsZero Trust → Team & Resources → Users
Review the planZero Trust → Settings → Cloudflare One plan

Revoking an existing session is not the same as removing a user from an Allow policy. Someone who remains allowed by a policy can authenticate again. Adjust the policy when permanently removing access.

9. Drawing the boundary between public and private access

The main challenge was not simply making a login page appear. It was understanding which Worker was protected, which URLs were included, and who could pass authentication.

For my private investment tool I chose All traffic. For a public blog with unpublished previews I chose Previews only. Separating those use cases let me avoid writing a custom login system without accidentally making the public blog private.

I am keeping these steps as a record of the setup, including the onboarding detours and the need to test alternative URLs and APIs.

Official references

CloudflareWebsite OperationsCloudflareZero TrustAccessWorkersPreviewSecurityMFAPrivate Website
Connected projectsBLOG
Practical KnowledgeKnowledge guide