How to Password-Protect a Staging Site for Free With Cloudflare Access

How to Password-Protect a Staging Site for Free With Cloudflare Access

Every freelancer and agency hits this at some point: a client staging site needs to go live on a real subdomain so the client can review it, but "live" shouldn't mean "indexed by Google and open to anyone who guesses the URL." The classic fix is HTTP Basic Auth — a username and password baked into your web server config. It works, but it means editing server config, it's easy to forget to remove before launch, and it does nothing if you also want to see who actually logged in and when.

If your DNS already runs through Cloudflare, there's a cleaner option that needs zero server config and zero code: Cloudflare Access, part of its Zero Trust platform, free for up to 50 users. You add a rule in a dashboard, and Cloudflare handles the login screen at the edge — before a single request reaches your origin server.

How this actually works

The difference between this and Basic Auth isn't just convenience — it's where the check happens:

Where Cloudflare Access checks a request vs Basic Auth Diagram comparing two request flows: with Cloudflare Access, the visitor is checked at Cloudflare's edge before ever reaching the origin server. With server-side Basic Auth, the unauthenticated request reaches the origin server first, which then challenges for credentials. Cloudflare Access (this guide) Visitor Cloudflare edge Access checks email + PIN only if allowed Origin server never sees blocked traffic Server-side Basic Auth Visitor Origin server sees every request first

With Access, an unauthenticated visitor's request never reaches your VPS at all — Cloudflare rejects it at the edge. With Basic Auth, every request still hits your origin server first, which then challenges for credentials; your server is doing work (and is technically reachable) even for requests that end up rejected.

Cloudflare Access vs the alternatives

ApproachSetup effortCostPer-person revokeBlocks traffic before origin
Cloudflare AccessDashboard only, ~10 minFree (≤50 users)YesYes
Server-side Basic AuthEdit server/.htaccess configFreeNo (shared password)No
Cloudflare Worker + Basic AuthWrite and deploy codeFree (generous tier)No (shared password)Yes
Full VPN (e.g. WireGuard)Server + client setupFree–lowYesYes

A VPN is the heavier-duty option — worth it if your team already lives inside one, like the private-networking setup in our guide to running multiple apps on one VPS with Docker and Caddy. For gating a single client-facing staging site, it's usually overkill: your client isn't going to install a VPN client to look at a homepage draft.

What you're actually setting up

Instead of a shared username and password, Access sends a one-time login code by email to whoever you've explicitly allowed. Your client gets a real login screen at your staging URL, enters their email, gets a 10-minute PIN in their inbox, and they're in — no account to create, no password to forget, no code to write or deploy.

Step 1: Confirm your domain is on Cloudflare

This only works if the domain (or subdomain) you're protecting already has its DNS managed by Cloudflare — Access can't be added to a domain managed elsewhere. If your staging subdomain is already proxied through Cloudflare (the orange cloud icon next to its DNS record), you're set.

Step 2: Turn on one-time PIN login

In the Cloudflare dashboard, go to Zero TrustSettingsAuthentication, and confirm One-Time PIN is enabled as a login method — see Cloudflare's official one-time PIN documentation for reference. New Zero Trust accounts don't always have it on by default — if it's off, this is the only setup step it needs; no external identity provider (Okta, Google Workspace, etc.) required.

Step 3: Create a Self-Hosted Access application

  1. Go to Access controlsApplicationsCreate new application (Cloudflare's self-hosted application docs cover every field in detail if you want the full reference).
  2. Select Self-hosted.
  3. Under Application domain, choose the domain, then the specific subdomain or path you want to protect — e.g. staging.yourclientsite.com, or just a subpath like /preview if you only need part of a site gated.
  4. Give it a clear name (something like "Acme Corp — Staging Review") so it's easy to find later when you're managing several client sites at once.

Step 4: Set the policy — who's actually allowed in

This is the part worth getting right, because a loosely configured rule quietly defeats the whole point:

  1. Add a policy with action Allow.
  2. Under Login methods, select One-Time PIN.
  3. Under Include, add a rule for Email and list the specific addresses allowed — your client contact, their team, and anyone on your side who needs access.

Skip that last step and Access will happily send a login code to any email address that asks for one — the gate exists, but it isn't actually locked. Scoping to specific addresses (or a specific email domain, like everyone @acmecorp.com) is what makes this an actual access control rather than a login screen anyone can walk through. Cloudflare's own guide to common Access policies covers this exact pitfall in more depth if you're setting up rules for a bigger team.

Step 5: Save and test it yourself first

Save the application, then open the staging URL in a private/incognito window. You should land on Cloudflare's Access login page before anything from your actual site loads. Enter an email address you added to the policy, grab the PIN from your inbox, and confirm you land on the real site afterward. Then try an email address you didn't add — it should be rejected. Cloudflare deliberately shows the same "a code has been emailed to you" message either way, so a rejected attempt won't announce itself; the real test is that no code arrives.

Frequently asked questions

Does this also stop Google from indexing my staging site? Access blocks unauthenticated visitors, including crawlers, from reaching the content — a bot can't index what it can't authenticate into. That said, it's still good practice to keep a noindex tag or a blocked robots.txt on staging as a second layer, in case the subdomain ever gets exposed another way (e.g. a misconfigured DNS change).

Can I use a shared password instead of individual emails? Not directly through the one-time PIN method described here — Access is built around verifying an identity (an email address), not a shared secret. If you specifically want a single shared password for a very informal case, that's what the older Worker-based Basic Auth approach is for; you lose per-person revocation and audit logging in exchange for simplicity.

Does this work on Cloudflare's free plan, or do I need Pro? Access is part of Cloudflare's separate Zero Trust product line, not the website Pro/Business plans — its own free tier (up to 50 users) works regardless of which plan your zone is on.

What happens when the project ships and I want to make the site public? Go back to the Access application and either delete it or pause the policy. There's no server config to remember to revert — unlike Basic Auth, where a forgotten .htaccess rule is a classic way client sites end up locked to the public by accident, or the reverse: accidentally left open.

Why this beats the Basic Auth / Workers approach for most freelancers and agencies

  • No server config, no code. Older tutorials for this exact problem walk through writing a Cloudflare Worker to inject Basic Auth headers — that works, but it's a script to maintain, test, and remember exists. This is dashboard configuration only.
  • No shared password to leak or forget to rotate. A Basic Auth password gets shared over Slack, saved in a browser, and outlives the project. Email-based access can be revoked for one person without resetting anyone else's access.
  • It's actually free at freelancer/agency scale. The 50-user cap on Cloudflare's free Zero Trust tier is per authenticated user, not per site — so if you're running staging sites for several clients, the same free allowance covers all of them, as long as your total reviewer count across everything stays under 50.

When to reach for something else instead

This is built for a small, known list of people — a client, their team, your own team. If you need self-serve signup, public registration, or you're gating an actual paid product rather than a staging preview, Access isn't the right tool; that's an authentication system built into your application, not an edge-level gate in front of it. If your staging environment sits behind SSH access you've already locked down — see our guide on disabling SSH password login on a VPS — Access is a complementary layer, not a replacement for that. And if the "staging site" is really a full internal admin panel or database tool your team uses daily, it's worth reading up on Cloudflare Access more broadly — this guide covers the specific, common case of gating a client-facing preview, not the full platform.

Setup steps and free-tier details in this guide were verified against Cloudflare's official Zero Trust documentation in August 2026.

Comments 0

Be the first to comment.

Leave a comment