Securely expose an OpenClaw Gateway WebUI on a VPS via Cloudflare Zero Trust Access + Cloudflare Tunnel (cloudflared), including DNS cutover for custom hostnames and optional cleanup of Tailscale Serve.
--- name: openclaw-cloudflare-secure description: Securely expose an OpenClaw Gateway WebUI on a VPS via Cloudflare Zero Trust Access + Cloudflare Tunnel (cloudflared), including DNS cutover for custom hostnames and optional cleanup of Tailscale Serve. --- # OpenClaw WebUI: Cloudflare Access + Tunnel (VPS) Use this when you want an easy public URL (e.g. `openclaw.example.com`) that is **NOT** directly exposed, protected by **Cloudflare Access allowlist**, and delivered via **Cloudflare Tunnel** to a local service (commonly `http://127.0.0.1:18789`). ## Assumptions - OpenClaw WebUI is reachable locally on the VPS at `http://127.0.0.1:18789` (or your chosen local port). - You control DNS for the zone in Cloudflare (e.g. `example.com`). - You have a Cloudflare API token available to the agent/VPS as `CLOUDFLARE_API_TOKEN`. - Recommended token perms (least privilege): **Zone:DNS:Edit** + **Zone:Zone:Read** for the target zone. - This is the key reason this setup is “agent-friendly”: the agent can securely create subdomains / manage DNS records without giving it full Cloudflare account access. - You can access Cloudflare Zero Trust UI to create: - an **Access Application** for the hostname - an **Allow** policy for specific emails - a **Block** policy for Everyone - a **Tunnel** and its **token** ## Quick start (copy/paste) ### 0) Optional: disable Tailscale Serve If you used Tailscale Serve earlier and want to remove it: ```bash sudo tailscale serve reset ``` ### 1) Install and start cloudflared tunnel service (token-based) In Cloudflare Zero Trust: - Networks → Connectors → Tunnels → Create tunnel → Cloudflared - Copy the token from the command `cloudflared service install <TOKEN>` On the VPS: ```bash ./scripts/install_cloudflared.sh sudo ./scripts/tunnel_service_install.sh '<TOKEN>' ``` Verify: ```bash sudo systemctl is-active cloudflared sudo systemctl status cloudflared --no-pager -l | sed -n '1,80p' ``` ### 2) DNS cutover: point hostname to the tunnel This uses the bundled DNS helper (`./scripts/cf_dns.py`). It will: - find and delete any existing A/AAAA/CNAME for that hostname - create a proxied CNAME to `<TUNNEL_UUID>.cfargotunnel.com` Prereq: ```bash export CLOUDFLARE_API_TOKEN='...' ``` ### 2b) (Optional) Create/update a subdomain / DNS record (agent-friendly) Use this when you want the agent (with least-privilege DNS token) to create records programmatically: ```bash ./scripts/dns_create_record.sh --zone example.com --type A --name openclaw --content 1.2.3.4 --proxied true ./scripts/dns_create_record.sh --zone example.com --type CNAME --name openclaw --content target.example.net --proxied true ``` ```bash ./scripts/dns_point_hostname_to_tunnel.sh \ --zone example.com \ --hostname openclaw.example.com \ --tunnel-uuid <TUNNEL_UUID> ``` ### 3) In Cloudflare Zero Trust UI: bind hostname → service In the tunnel: - Add Public Hostname: - Hostname: `openclaw.example.com` - Service: `http://127.0.0.1:18789` ### 4) Cloudflare Access policy In Zero Trust: - Access → Applications → Add → Self-hosted - Public hostname: `openclaw.example.com` - Policies: 1) Allow: include specific emails (your allowlist) 2) Block: include Everyone ## Notes / gotchas - If the Tunnel “route traffic” wizard errors with “record already exists”, it’s just DNS collision. Either: - delete the existing DNS record and let the wizard recreate it, OR - keep DNS as-is and set the Public Hostname mapping inside the Tunnel. - Keep the hostname **proxied** (orange cloud). Access/Tunnel require proxy. ## Rollback - DNS: point the hostname back to an origin A record (or remove the record). - VPS: `sudo systemctl disable --now cloudflared`.
don't have the plugin yet? install it then click "run inline in claude" again.