Locking the site down

The site is public to anyone who has the URL. Two ways to close it, depending
on whether you want an email code or a password you can hand to someone.


Option A — Cloudflare Access (email PIN)

No password to remember or leak. You enter your email, Cloudflare mails a
one-time code, you’re in for the session length you set. Free up to 50 users.

Setup

  1. Cloudflare dashboard → Workers & Pages → your project → Settings →
    General → find Access policy → Enable.
  2. That opens Zero Trust and pre-fills an application. First time through it
    asks for a team name — this becomes your login domain
    (your-team.cloudflareaccess.com). Pick something you’ll recognise.
  3. Under Policies, set Action to Allow, then Include →
    Emails → your address.
  4. Set Session duration. 24 hours or a week is sensible; you’re re-auth’ing
    on every device otherwise.
  5. Create.

The part that catches people

!!! warning “The default only covers previews”
The auto-created application uses a wildcard subdomain (*), which
protects preview deployments like a1b2c3.shan-network-blog.pages.dev —
not your production shan-network-blog.pages.dev.

To cover production, go to Zero Trust → **Access** → **Applications** and
either edit the application's hostname to the bare production domain, or
add a second application for it.

Verify

Open the site in an incognito window. You should get Cloudflare’s email
prompt, not your notes. Testing in your normal browser proves nothing — you
already have a session.

If the PIN email doesn’t arrive, check spam, then confirm the email in the
policy matches exactly.


Option B — A shared password

Cloudflare Pages has no native password option, but a Pages Function can do
HTTP Basic Auth in about twenty lines. Use this if you want to hand the URL and
a password to someone without adding them to an access list.

1. Create the function

At the repo root (not inside docs/):

mkdir functions
code functions/_middleware.js

_middleware.js runs before every request. Paste:

export const onRequest = async ({ request, env, next }) => {
  const header = request.headers.get("Authorization");

  if (header) {
    const [scheme, encoded] = header.split(" ");

    if (scheme === "Basic" && encoded) {
      const decoded = atob(encoded);
      const split = decoded.indexOf(":");
      const user = decoded.slice(0, split);
      const pass = decoded.slice(split + 1);

      if (user === env.SITE_USER && pass === env.SITE_PASSWORD) {
        return next();
      }
    }
  }

  return new Response("Authentication required.", {
    status: 401,
    headers: {
      "WWW-Authenticate": 'Basic realm="notes", charset="UTF-8"',
    },
  });
};

2. Set the credentials

Cloudflare dashboard → project → Settings → Variables and Secrets →
add two, as Secret (encrypted), not plaintext:

Name Value
SITE_USER whatever you like
SITE_PASSWORD a long random string

Add them to Production. Add to Preview too if you want previews covered.

3. Fail closed

In the project’s Functions settings, set the behaviour to fail closed.
If the function can’t run — daily request limit, an error — failing open would
serve the site unprotected. Failing closed serves an error instead. Wrong
answer is better than no lock.

4. Deploy and verify

git add functions/
git commit -m "feat: basic auth"
git push

Wait for the build, then open the site in an incognito window. You should
get the browser’s own username/password dialog.

Caveats

  • Credentials go over HTTPS, so they’re encrypted in transit — but Basic Auth
    sends them on every request, and the browser caches them until you close it.
    There’s no logout button.
  • Anyone with the password has full access. Rotating it means editing the
    secret and redeploying.
  • Never put the password in the repo. That’s what the secrets are for.

Which to pick

Access Basic Auth
Setup Dashboard only 20 lines + 2 secrets
Login Email PIN Username + password
Sharing Add each person’s email Hand over the password
Revoking Remove the email Change the password for everyone
Runs on Cloudflare’s edge auth Your Pages Function

Just you: Access — nothing to leak, nothing to maintain.
A study group: Basic Auth — one credential, no list to manage.


Reverting

Access: Zero Trust → Access → Applications → delete the application. Effective
immediately.

Basic Auth: delete functions/_middleware.js, commit, push. Live after the
next build.