I use Cloudflare tunnels a lot now that most of my coding agents run on my Mac Mini and I can't access localhost directly.
It works very well to ask your clanker “give me the cf url for the dev server” and it just understands and does it.
But it turns out Google indexes those URLs:
Set up a protected preview of my local dev server on Cloudflare.
Inputs
- Local server: http://127.0.0.1:PORT
- Public hostname: preview.EXAMPLE.COM (the zone is already on Cloudflare)
- Allowed login emails: [email protected]
- Tunnel name: SITE-preview
Rules
- Do not change my production Worker, routes, DNS for the apex, sitemaps, or robots.
- Do not write custom auth code. Cloudflare Access handles login.
- Configure Access before creating the DNS record, so the hostname is never public without login.
- Do not commit anything. Tunnel config lives in ~/.cloudflared, not in the repo.
- Do not overwrite existing tunnel config or credentials. Reuse matching settings, or use a separate config file and pass its path with --config when validating and running the tunnel.
- Stop and tell me if a step needs the dashboard or a login you cannot do.
Steps
1. Check the local server answers: curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:PORT/ must be 200.
2. Install cloudflared if missing (brew install cloudflared).
3. Check whether Zero Trust is enabled on the account that owns the zone (GET /accounts/{id}/access/apps). If it returns access.api.error.not_enabled, tell me to enable it at one.dash.cloudflare.com (team name, Free plan) and wait.
4. Create the named tunnel. Prefer cloudflared tunnel login then cloudflared tunnel create SITE-preview. If no browser is available, POST /accounts/{id}/cfd_tunnel with name, a base64 32-byte tunnel_secret, and config_src "local", then write ~/.cloudflared/<tunnel-id>.json with AccountTag, TunnelSecret, TunnelID (mode 600).
5. Write the chosen config file with tunnel, credentials-file, and ingress: hostname preview.EXAMPLE.COM -> http://127.0.0.1:PORT, then a catch-all http_status:404. Run cloudflared tunnel ingress validate.
6. Add One-time PIN as a login method (Zero Trust > Settings > Authentication > Login methods).
7. Add a self-hosted Access application for preview.EXAMPLE.COM, all paths, 24h session, accepting only One-time PIN, instant auth redirect on. Add one policy: Allow, Include Emails = the allowed emails.
8. Only now add the DNS record: proxied CNAME preview -> <tunnel-id>.cfargotunnel.com (or cloudflared tunnel route dns SITE-preview preview.EXAMPLE.COM).
9. Run cloudflared tunnel run SITE-preview in a terminal I can see and wait for four "Registered tunnel connection" lines.
10. Verify: curl -sD - https://preview.EXAMPLE.COM/ and one inner path must return 302 to https://<team>.cloudflareaccess.com/cdn-cgi/access/login/... without returning local page content. Do not follow redirects or send login cookies. Confirm the local server still answers.
11. Report the preview URL, the team name, what you verified, and what I still need to do (my own login test). Remind me to keep the tunnel and dev server running while testing; automatic startup is optional.