Loading
Loading
A real cloudflared tunnel, a real Access policy, and the actual commands — free for up to 50 users, no VPN client required on the visiting side.
Free Plan Seats
0
Users, forever free, per Cloudflare's plans page
Paid Tier
$7/user/mo
Pay-as-you-go, no seat cap
Open Ports Needed
0
cloudflared makes an outbound-only connection
Setup Time
~15 min
Tunnel + one Access application
Why Not Just Use A VPN
I covered self-hosted VPN protocols in my WireGuard vs OpenVPN benchmark, and a VPN is still the right tool when you need full network-level access — SSH into anything, reach any port on any host on the subnet. But most of the time what you actually want is narrower: let specific people reach one specific web app, without opening a port on your router or handing out network-wide access. Cloudflare Access (part of the broader Cloudflare Zero Trust / Cloudflare One product) does exactly that: it puts an identity check in front of a single hostname, and the tunnel connecting your server to Cloudflare is outbound-only, so there's no inbound port to expose at all.
What's Actually Free
Per Cloudflare's published Zero Trust & SASE plans page, the Free plan covers up to 50 users at $0/month and includes Zero Trust Network Access (what this guide sets up), Secure Web Gateway, DNS filtering, basic device posture checks, and 24 hours of Access log retention with limited data-loss-prevention features. A “user” consumes a seat on any authentication event — an Access login or a WARP device enrollment — and holds that seat until removed, so for a solo developer protecting a handful of personal apps, 50 seats is a very comfortable ceiling. Once you outgrow it, the Pay-as-you-go tier is $7 per user per month (billed annually), removes the seat cap, extends log retention to 30 days, and adds predefined DLP policies.
Published limits: Cloudflare Zero Trust Free vs Pay-as-you-go
Step 1 — Create The Tunnel
Install cloudflared, the daemon that opens the outbound connection from your server to Cloudflare's edge. Then authenticate it against your Cloudflare account and create a named tunnel:
cloudflared tunnel login
cloudflared tunnel create home-serverThe createcommand writes a credentials JSON file and prints the tunnel's ID. Next, define which internal service maps to which public hostname in a config file (commonly ~/.cloudflared/config.yml):
tunnel: home-server
credentials-file: /home/you/.cloudflared/<TUNNEL-ID>.json
ingress:
- hostname: app.yourdomain.com
service: http://localhost:8080
- service: http_status:404That last line is not optional decoration — per Cloudflare's own configuration-file documentation, every ingress block must end with a catch-all rule with no hostname field, and cloudflared will refuse to start without one. It's the single most common first-run error, and now you won't hit it. Validate the config and start the tunnel:
cloudflared tunnel --config config.yml ingress validate
cloudflared tunnel run home-serverStep 2 — Protect It With Access
Cloudflare's own documentation is explicit about ordering here: create the Access application first, because a published route with no Access application in front of it is reachable by anyone on the internet. In the Zero Trust dashboard, under Access controls → Applications, add a self-hosted application for app.yourdomain.com, then attach a policy. The simplest policy for a solo developer is an email allow-list — under Access controls → Policies, set the decision to Allow and the include rule to your own email address (or your whole email domain, if you want anyone on it in):
# Policy structure (as configured in the dashboard or via Terraform)
decision: allow
include:
email: ["you@yourdomain.com"]With no existing identity provider connected, Cloudflare falls back to one-time-PIN login — visitors get a login code emailed to them, no separate account required. Finally, back in the tunnel's public hostname settings, enable “Protect with Access” so cloudflared itself validates the Access token on every request, rather than relying on your origin app to check it.
When This Isn't Enough
This setup is for HTTP(S) applications — a Grafana dashboard, an admin panel, a personal wiki. It doesn't replace a VPN for raw SSH, database ports, or full-subnet access; for that, Cloudflare's WARP client can tunnel arbitrary TCP through the same Zero Trust account, or you fall back to a dedicated protocol like the WireGuard setup covered separately on this site. The practical pattern most solo developers land on: Access in front of every web UI you self-host, and a VPN (or WARP) reserved for the handful of things that genuinely need raw network access.
None of the commands above are hypothetical — they're the same commands and config structure documented at developers.cloudflare.com, just condensed into the fifteen minutes it actually takes to go from an open service to one that checks identity before letting anyone in.

Written by Abhishek Kushwaha
Founder and writer at Global Tech Search, based in Kathmandu, Nepal. Covers AI, infrastructure, markets, and climate with sourced data and original analysis. More about the author →
Recommended Tools
Affiliate disclosure — we may earn a commission at no cost to you.
Sources
Commands and config shown here match Cloudflare's own documentation structure as of publishing; Cloudflare occasionally renames dashboard sections and adjusts free-tier limits, so check developers.cloudflare.com/cloudflare-one for the current dashboard layout before following this step by step.
Global Tech Search
More independent, cited breakdowns of the infrastructure decisions that actually matter.
Back to Dashboard →