# Cloudflare Pages Password Protection with Access

> Protect Cloudflare Pages previews, production URLs, and custom domains with Cloudflare Access, email one-time PINs, and identity policies.

Cloudflare Pages uses Cloudflare Access to restrict visitors through identity checks, such as an email one-time PIN or an organization login. It does not have a standard shared site-password setting.

Configure protection for each hostname that can serve your content. A protected preview does not mean the production site is private.

## Choose who can visit

| Login method | Suitable audience |
|---|---|
| Email one-time PIN | Named visitors who can receive an email code |
| Organization identity provider | People with an approved work account |

Create an Access **Allow** policy for the intended email addresses, domains, or identities. For email codes, follow Cloudflare’s [one-time PIN setup](https://developers.cloudflare.com/cloudflare-one/integrations/identity-providers/one-time-pin/). Your Cloudflare account password controls the dashboard, not access to the website.

## Protect preview deployments

1. Open the Pages project in Cloudflare.
2. Go to **Settings > General** and select **Enable access policy**.
3. Open the resulting Access application and review its Allow policy.
4. Test a preview URL with an approved and an unapproved account.

The Pages toggle protects preview hostnames such as ` . .pages.dev`. It does **not** protect ` .pages.dev` or a custom domain. Cloudflare describes this distinction in its [preview deployment documentation](https://developers.cloudflare.com/pages/configuration/preview-deployments/).

## Protect the production pages.dev address

Follow Cloudflare’s [production Access instructions](https://developers.cloudflare.com/pages/platform/known-issues/#enable-access-on-your-pagesdev-domain):

1. Enable the Pages access policy, then select **Manage Access policy**.
2. Find the project in **Access > Applications** and choose **Configure**.
3. In its public hostname, remove the wildcard `*` from the subdomain so the application covers ` .pages.dev`. Save.
4. Return to the Pages settings and enable the access policy again to restore preview protection.
5. Verify that both the production hostname and `*. .pages.dev` have Access coverage and the intended Allow policies.

The production address and the wildcard previews are separate entries. Check both after changing either application.

## Protect a custom domain

In the Zero Trust dashboard, open **Access controls > Applications**, create a **Self-hosted and private** application, add the custom domain as its public hostname, and configure the Allow policy. Cloudflare includes this flow in its [Pages Access instructions](https://developers.cloudflare.com/pages/platform/known-issues/#enable-access-on-your-pagesdev-domain).

Do not stop after protecting the custom domain: the original `pages.dev` addresses can still provide another route to the same content.

## Test every route to the site

Make a short list of production, preview, branch, and custom-domain URLs before testing. Use a private browser window without an existing Access session.

- An approved visitor should reach the site after signing in.
- An unapproved visitor should be denied.
- A direct link to a PDF, image, or other private asset should require the same access check.
- Old deployment links should follow the intended policy too.

If a URL opens without authentication, inspect the application’s hostname and path coverage before sharing it. Repeat the check after adding a domain or changing policies.

## Avoid browser-only password screens

A JavaScript prompt can hide a page visually while leaving its files downloadable. Authentication needs to happen before private content is served. A custom Worker can implement that check, but then you are responsible for protecting every request and maintaining the authentication code.

Get Email address for your AI agent

Create a cloud bucket and copy its @revdokumail.com email address.

Emails sent to this email are saved as JSON and Markdown, attachments are extracted.

Connect AI agents to read and manage saved emails and files via the API, CLI, MCP, or Skill.

Create Free Account or connect your AI agent

Connect AI:

Prompt
Skill
MCP
API

Show full prompt ⌄

Copy

npx skills add revdoku/revdoku --skill revdoku -g

Copy

Claude Code
Codex
Cursor
Gemini CLI
Hermes Agent
OpenClaw

Codex CLI Claude Code Other AI app

Codex CLI
Claude Code
Other

codex mcp add revdoku --url https://app.revdoku.com/mcp && codex mcp login revdoku Copy

claude mcp add --transport http revdoku https://app.revdoku.com/mcp && claude mcp login revdoku Copy

Transport Streamable HTTP Auth Browser OAuth

https://app.revdoku.com/mcp Copy

Open the MCP setup guide &rarr;

JavaScript
Python
C# (.NET)

const apiKey = process.env.REVDOKU_API_KEY; // Get a key at https://app.revdoku.com/account/access
if (!apiKey) throw new Error("Set REVDOKU_API_KEY first");

const response = await fetch(
"https://app.revdoku.com/api/v1/buckets", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ bucket: { title: "My agent inbox" } })
}
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
const { data } = await response.json();
const inbox = data.bucket.inbound_email;
console.log("Bucket ID:", data.bucket.id);
console.log("Email:", inbox.address);
console.log("Ready:", inbox.ready);
console.log("Open:", data.bucket.dashboard_url);
if (!inbox.ready) console.log("Receiving:", inbox.blocked_reason);
Copy code

JavaScript setup & full example →

import os
import requests

api_key = os.environ["REVDOKU_API_KEY"] # Get a key at https://app.revdoku.com/account/access
response = requests.post(
"https://app.revdoku.com/api/v1/buckets",
headers={"Authorization": f"Bearer {api_key}"},
json={"bucket": {"title": "My agent inbox"}},
timeout=30,
)
response.raise_for_status()
bucket = response.json()["data"]["bucket"]
inbox = bucket["inbound_email"]
print("Bucket ID:", bucket["id"])
print("Email:", inbox["address"])
print("Ready:", inbox["ready"])
print("Open:", bucket["dashboard_url"])
if not inbox["ready"]:
print("Receiving:", inbox["blocked_reason"])
Copy code

Python setup & full example →

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

var apiKey = Environment.GetEnvironmentVariable("REVDOKU_API_KEY") // Get a key at https://app.revdoku.com/account/access
?? throw new Exception("Set REVDOKU_API_KEY first");
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", apiKey);
using var response = await client.PostAsJsonAsync(
"https://app.revdoku.com/api/v1/buckets",
new { bucket = new { title = "My agent inbox" } });
response.EnsureSuccessStatusCode();
using var json = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
var bucket = json.RootElement.GetProperty("data").GetProperty("bucket");
var inbox = bucket.GetProperty("inbound_email");
Console.WriteLine($"Bucket ID: {bucket.GetProperty("id").GetString()}");
Console.WriteLine($"Email: {inbox.GetProperty("address").GetString()}");
Console.WriteLine($"Ready: {inbox.GetProperty("ready").GetBoolean()}");
Console.WriteLine($"Open: {bucket.GetProperty("dashboard_url").GetString()}");
if (!inbox.GetProperty("ready").GetBoolean())
Console.WriteLine($"Receiving: {inbox.GetProperty("blocked_reason").GetString()}");
Copy code

C# (.NET) setup & full example →

## Frequently Asked Questions

### Can I protect a Cloudflare Pages site with one shared password?

Cloudflare Pages has no standard shared-password setting. Use Cloudflare Access with email one-time PINs or an identity provider, and define who is allowed to visit.

### Does Enable access policy also protect my production Pages site?

No. The Pages toggle protects preview hostnames. The production project.pages.dev address and custom domains need separate Access coverage. Test every hostname that can serve the site.

### Does changing my Cloudflare account password protect my Pages site?

No. Your account password controls access to the Cloudflare dashboard, not who can visit a `pages.dev` or custom-domain website. Visitor restrictions must be configured separately through Cloudflare Access.

### How can I restrict access to specific people or organizations?

Create an Access Allow policy for individual email addresses, approved email domains, or identities supplied by your organization’s login provider. Test the policy with both an authorized and an unauthorized account before sharing the site.

### Do I need to protect both the custom domain and the pages.dev address?

Yes, if both addresses can serve the site. An Access policy covering only one hostname may leave the other publicly reachable. Verify every active hostname and any sensitive paths in a private browsing session.

### When should I use an email one-time PIN instead of an identity provider?

An email one-time PIN works well for lightweight access when visitors do not share an organization login. An identity provider such as Google, Microsoft, or Okta is generally better for managed team access and centralized account control.

### Can JavaScript or a Cloudflare Worker hide private site content?

Client-side JavaScript cannot securely protect files because visitors may retrieve the underlying assets directly. A Worker can provide authentication only if it validates every request before returning protected content, making it a security-sensitive implementation. Cloudflare Access is usually the safer native choice.

---

[View the canonical page](https://revdoku.com/blog/how-to-password-protect-a-cloudflare-pages-site/) · [Browse llms.txt](https://revdoku.com/llms.txt)
