Deploy Private Cloudflare Site
SkillWeb & browsingDeploy, protect, debug, and verify a private Cloudflare Workers site for an exact user or email allowlist. Use for Workers or framework sites that must require Cloudflare Access, Google OAuth, or another SSO boundary; when protecting Workers Static Assets and framework chunks; when Wrangler/headless OAuth, secrets, workers.dev routes, errors 1042/1101, or unhydrated pages cause trouble; and when proving that anonymous users cannot retrieve sensitive HTML or assets.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Deploy Private Cloudflare Site skill
What this skill tells your AI
The instructions your AI receives, as published by amanaiproduct/amans-skills in skills/deploy-private-cloudflare-site/SKILL.md and read by ahel’s review.
Deploy a site behind a fail-closed identity boundary, preserve framework asset delivery after authentication, and verify the public surface before handing it off.
Non-negotiable rules
- Treat private content, source data, build artifacts, and identity-provider credentials as sensitive.
- Prefer Cloudflare Access attached to the Worker. Use application-managed Google OAuth only when Access is unavailable, unsuitable, or explicitly requested.
- Keep the production route disabled or return a fail-closed maintenance response until the identity boundary and required secrets exist.
- Never place client secrets, signing keys, or API tokens in
vars, source files, command-line arguments, logs, or chat output. Usewrangler secret putthrough interactive or file-based input. - Do not fabricate API tokens or ask for account passwords. Use Wrangler OAuth, the Cloudflare API MCP OAuth flow, or a narrowly scoped token the user creates.
- Require explicit authorization before transferring an identity-provider secret from one service to Cloudflare.
- Do not declare success from a local build alone. Test the deployed anonymous boundary and one authenticated browser session.
- Retrieve current official Cloudflare and identity-provider documentation before changing live configuration. Cloudflare's Workers and Access surfaces evolve quickly.
Load the focused references
- Read references/workflow.md before choosing the protection architecture or changing live resources.
- Read references/troubleshooting.md when deployment succeeds but requests fail, assets 404, or the page does not hydrate.
- Run
scripts/verify-private-boundary.mjsbefore handoff.
Workflow
1. Inspect before mutating
- Find repository instructions and inspect the working tree without overwriting unrelated changes.
- Identify the framework, build command, build output, Wrangler version, existing
wrangler.jsoncor generated config, routes, asset binding, and current public URLs. - Check Cloudflare identity non-destructively with
wrangler whoami. If unavailable, usewrangler login; use--browser=falseor device flow in a headless environment. - Check whether the Cloudflare plugin or Cloudflare API MCP is installed. Use it when available for account configuration, but keep Wrangler for build/deploy/tail operations.
- Enumerate every public path: production hostname,
workers.dev, preview URLs, custom domains, static chunks, API endpoints, source maps, and old hosts. - Record the intended allowlist and whether each identity is a Gmail, Workspace, or non-Google address. Do not assume a private-relay address can securely authenticate through Google.
2. Choose one protection architecture
Use this order:
- Worker-level Cloudflare Access: preferred because every domain and preview attached to the Worker can be protected together.
- Hostname/path Access: use when only a specific route or custom hostname should be private, or when WebSockets make Worker-level Access unsuitable.
- Application-managed OAuth: use only after documenting why Access is not being used. This adds callback, CSRF, token-validation, session, and asset-gating responsibilities to the application.
For exact people in Access, create an Allow policy whose Include selector is Emails with the complete addresses. Do not use Everyone, Login Methods: One-time PIN, or a broad email-domain rule when the requirement is an exact allowlist.
For a non-Google address, Cloudflare Access with email one-time PIN can authenticate ownership without pretending Google is authoritative for that address. If stronger assurance is required, configure its actual identity provider.
3. Build a fail-closed Worker boundary
For Cloudflare Access:
- Attach Access to the Worker or exact hostname before enabling the public route.
- Use
ctx.accessonly when application code needs the authenticated identity; Access itself should reject unauthorized requests before the Worker runs. - Confirm the policy is deny-by-default and includes only intended addresses.
For application-managed Google OAuth:
- Use the authorization-code flow with an exact HTTPS redirect URI.
- Generate and verify
state; usenonceand PKCE where supported. - Exchange the code server-side. Validate the ID token signature,
iss,aud,exp, nonce, and verified identity claims against current Google guidance. - Normalize the email for allowlist comparison, but use the Google
subclaim as the stable account identifier in stored user records. - For Gmail, Google is authoritative for the address. For Workspace, require
email_verifiedand the expectedhdclaim. For non-Gmail addresses withouthd, flag that Google does not provide continuing authority over the underlying mailbox. - Sign the application session with a dedicated random secret. Set cookies
Secure,HttpOnly, andSameSite=Laxor stricter; set explicit expiry; rotate deliberately. - Return a generic 403 for a valid Google account not on the application allowlist.
4. Gate HTML and assets together
When Workers Static Assets are present:
- Configure an
ASSETSbinding andassets.run_worker_first: truewhen application code performs authentication. - Authenticate first, then route actual static files through
env.ASSETS.fetch(request). - Do not send framework chunks such as
/_next/static/*,/assets/*, fonts, CSS, or client manifests through a framework request handler that may return a 404 instead of falling through to the asset binding. - Keep only the minimum sign-in and OAuth callback surface public. Verify that public responses contain no private page text, serialized state, filenames, metadata, or user data.
- Prefer route-pattern
run_worker_firstconfiguration when it can express the boundary without a wrapper.
5. Configure secrets and deploy safely
- Declare required secret names with
secrets.requiredwhen the installed Wrangler version supports it. - Add secrets interactively with
wrangler secret put NAME. Do not pass values as shell arguments. - Use a dry run or build before deployment. Inspect the generated Wrangler configuration when using the Cloudflare Vite plugin or another adapter.
- Deploy while
workers.devremains disabled if the boundary is incomplete. Remember that Wrangler can re-enable it when config and dashboard disagree. - Enable only the intended route after Access or the OAuth wrapper is ready.
- Preserve a rollback target or previous Worker version.
6. Verify the privacy boundary
Run the bundled anonymous verifier with at least one real deployed asset path and several private phrases:
node ~/.codex/skills/deploy-private-cloudflare-site/scripts/verify-private-boundary.mjs \
--url https://example.workers.dev \
--asset /assets/app.js \
--forbid "private dashboard" \
--forbid "customer name"
Then verify manually in a clean browser context:
- Anonymous root is redirected to sign-in or returns 401/403.
- The public sign-in response contains none of the private phrases.
- A real JavaScript/CSS/image path is also redirected or denied, not 200 or 404.
- An allowlisted identity completes sign-in and the app hydrates.
- A non-allowlisted identity is denied.
- Sign-out and expired/tampered sessions fail closed.
- Test every alternate hostname, preview URL, and old deployment.
- Confirm mobile layout and core navigation after hydration.
Do not use a guessed asset path: an anonymous 404 proves only that the path is absent.
7. Diagnose production failures with evidence
- Use
wrangler tailor Workers Logs immediately for 1101/exception responses. - Inspect browser Network and Console panels when HTML loads but navigation or tabs do not work.
- Compare response status, content type, cache headers, and body for HTML and a known framework chunk.
- Test the smallest failing deployed request before changing architecture.
- Apply the error-specific checks in references/troubleshooting.md.
8. Handoff
Report:
- the canonical private URL and every other route's disposition;
- the protection architecture and exact allowlist, without secrets;
- build, tests, deploy version, anonymous verification, and authenticated verification;
- any OAuth testing-mode expiry, verification requirement, Access plan constraint, or non-Google identity caveat;
- rollback instructions and where future agents should update the allowlist.
Never state that a site is private merely because its root redirects. Privacy requires testing real assets and alternate routes too.
Signals
- GitHub stars
- 79
- Forks
- 16
- Last commit
- Sep 2026
ahel review
S4info
community integration, published by amanaiproduct, not cloudflareK6low
bundled executables the agent is told to run
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Item type
- skill
- Key
deploy-private-cloudflare-site- Source
- github.com/amanaiproduct/amans-skills