Expose a service running on your local machine to a remote server without opening any ports. For instance, let your OpenClaw agent (the remote server) access qBittorrent Web UI on your Mac (the local machine), to download a movie for you.
The local machine makes an outbound-only connection to Cloudflare. The remote server hits your subdomain on Cloudflare's edge. Traffic flows:
OpenClaw on your remote server -> https://your-tunnel-name.example.com -> Cloudflare edge servers -> Cloudflare Tunnel -> qBittorrent Web UI on your local machine
You can probably do the same thing with Tailscale, but unfortunately, Tailscale app doesn't work well with Mullvad VPN on macOS (and I don't want to use Tailscale's Mullvad VPN add-on).
ref:
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/
https://tailscale.com/docs/features/exit-nodes/mullvad-exit-nodes
Setup
1. Create Cloudflare Tunnel
Do this from any device where you're logged into Cloudflare. No login needed on the local machine or the remote server.
- Go to Cloudflare Zero Trust dashboard
- Networks -> Connectors -> Create a tunnel -> Cloudflared
- Name your tunnel:
your-tunnel-name
- Name your tunnel:
- Copy the tunnel token
- Configure the tunnel you just created -> Published application routes -> Add a published application route
- Subdomain:
your-tunnel-name - Domain: select your domain from the dropdown (e.g.,
example.com) - Path:
[leave empty] - Service:
- Type:
HTTP - URL:
localhost:8080
- Type:
- Subdomain:
- After you create the published application route, Cloudflare will automatically create the DNS record for your subdomain
ref:
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/
https://developers.cloudflare.com/cloudflare-one/networks/routes/add-routes/
2. Access Controls for Cloudflare Tunnel
Still in the Cloudflare Zero Trust dashboard.
- Access controls -> Service credentials -> Service Tokens -> Create Service Token
- Token name:
your-token-name - Service Token Duration:
Non-expiring - Save the
CF-Access-Client-IdandCF-Access-Client-Secret(shown only once)
- Token name:
- Access controls -> Policies -> Add a policy
- Policy name:
your-policy-name - Action:
Service Auth - Session duration:
24 hours - Configure rules -> Include:
- Selector:
Service Token - Value: select the service token you just created (e.g.,
your-token-name)
- Selector:
- Policy name:
- Access controls -> Applications -> Add an application -> Self-hosted
- Application name:
your-tunnel-name - Session Duration:
24 hours - Add public hostname:
- Input method:
Default - Subdomain:
your-tunnel-name(must match the subdomain in step 1.4) - Domain: select your domain from the dropdown (e.g.,
example.com) - Path:
[leave empty]
- Input method:
- Select existing policies (this text is a clickable button, not a label!)
- Check the policy you created in step 2.2
- Application name:
ref:
https://developers.cloudflare.com/cloudflare-one/access-controls/service-credentials/service-tokens/
https://developers.cloudflare.com/cloudflare-one/access-controls/policies/
https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/
3. Run cloudflared on Local Machine (macOS)
Make cloudflared run on boot, connecting outbound to Cloudflare. No browser auth ever needed.
brew install cloudflared
# install as a LaunchAgent using the tunnel token from step 1
sudo cloudflared service install YOUR_TUNNEL_TOKEN
ref:
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/
To verify it's running:
sudo launchctl list | grep cloudflared
4. Access the Local Service on Remote Server
Test that the tunnel and access policy work. We're accessing qBittorrent Web UI here:
curl \
-H "CF-Access-Client-Id: $YOUR_CF_ACCESS_CLIENT_ID" \
-H "CF-Access-Client-Secret: $YOUR_CF_ACCESS_CLIENT_SECRET" \
-d "username=YOUR_USERNAME&password=YOUR_PASSWORD" \
https://your-tunnel-name.example.com/api/v2/auth/login
The CF-Access-XXX headers must be included on every request. Without them, Cloudflare returns a 302 redirect to a login page.
ref:
https://github.com/qbittorrent/qBittorrent/wiki/#webui
Why Cloudflare Tunnel Over Tailscale
- No login on endpoints: The tunnel token is scoped to one tunnel, can't access your Cloudflare account
- No VPN conflicts:
cloudflaredis just outbound HTTPS, Mullvad VPN doesn't care - Free: Cloudflare Zero Trust free tier covers this


