No inbound ports, ever
Nothing is exposed on your router. The connector makes an outbound TLS connection; the edge multiplexes requests down it. Port scans find nothing.
Self-hosting over a Cloudflare Tunnel
This page is not on a hosting platform. It is a plain Node process bound to
127.0.0.1:19000 on one machine, reached through an
outbound-only tunnel that publishes it as chokita.com.
No public IP. No port forwarding. No router surgery. No monthly bill.
The only arrow that leaves your network starts inside it. That is the whole trick — an outbound connection is something every firewall already allows.
Any stack works — Node, Python, Go, PHP, a container, a dev server. If it listens on a local port and speaks HTTP, it can be published like this.
Bind it to loopback or 0.0.0.0 on the machine that will
host it. Port 19000 in this case. It never needs a
firewall rule — nothing external talks to it directly.
node server.js
# [chokita] listening on http://0.0.0.0:19000
cloudflared links to your Cloudflare account and mints a
tunnel that belongs to it. A named tunnel keeps its identity forever — unlike a
quick tunnel, its URL does not change every restart.
cloudflared tunnel login
cloudflared tunnel create chokita
# Created tunnel chokita with id 6f1a…-… (credentials file written locally)
One command creates the proxied DNS record. Traffic for the hostname now resolves to Cloudflare's edge, which knows to hand it to your connector.
cloudflared tunnel route dns chokita chokita.com
Ingress rules map the hostname to the local service. Run it as a service so it survives reboots and reconnects itself.
# ~/.cloudflared/config.yml
tunnel: chokita
credentials-file: /home/you/.cloudflared/6f1a….json
ingress:
- hostname: chokita.com
service: http://127.0.0.1:19000
- service: http_status:404
The economics changed. A tunnel replaces most of what you used to rent a host for.
Nothing is exposed on your router. The connector makes an outbound TLS connection; the edge multiplexes requests down it. Port scans find nothing.
Mobile, fibre, a cafe, a VPS with a locked-down firewall — if the box can reach the internet, it can serve. No public IPv4 needed.
A real certificate for your domain, issued and renewed automatically. Your app deals in plain HTTP on loopback.
DDoS absorption, WAF rules, rate limiting, bot checks, caching and access policies sit in front of your box before a request reaches it.
Files, database, logs and secrets never leave the machine you control. The edge sees requests, not your disk.
An old desktop, a mini PC, a Raspberry Pi. The tunnel itself is free. The recurring bill becomes electricity.
Two ways in. Token mode is the one to automate — it is a single secret string and needs no credentials file on disk. Local mode is nicer while you are learning on your own machine.
Get a connector token from the Cloudflare dashboard (Zero Trust → Networks → Tunnels → your tunnel → Configure → Install connector). Treat it like a password: environment variable or secret store, never committed.
# 1. run the app on 19000
node server.js &
# 2. hand cloudflared the token — no credentials file required
export TUNNEL_TOKEN="eyJhIjoi…" # PowerShell: $env:TUNNEL_TOKEN="eyJhIjoi…"
cloudflared tunnel run --token "$TUNNEL_TOKEN"
Why token mode wins: the token encodes the tunnel identity and the ingress rules. Rotate it in the dashboard and every connector picks up the change. It is also the shape that scales — the same token on a second machine gives you a second replica.
Credentials live in a JSON file next to config.yml. Good for a
single always-on box you administer by hand.
cloudflared tunnel login
cloudflared tunnel create chokita
cloudflared tunnel route dns chokita chokita.com
cloudflared tunnel run chokita # reads ~/.cloudflared/config.yml
tunnel: chokita
credentials-file: C:\Users\you\.cloudflared\6f1a….json
originRequest:
connectTimeout: 30s
noTLSVerify: false
ingress:
- hostname: chokita.com
service: http://127.0.0.1:19000
- hostname: www.chokita.com
service: http://127.0.0.1:19000
- service: http_status:404
No account, no DNS, no config. You get a random *.trycloudflare.com
URL that dies with the process. Perfect for showing someone your laptop's dev server for
ten minutes. Not for anything you intend to keep.
cloudflared tunnel --url http://localhost:19000
# https://random-words-1234.trycloudflare.com ← changes on every restart
Never put real user data behind a quick tunnel — the hostname is unguessable but public, and it is gone the moment you close the terminal.
A tunnel is only as available as the process and the machine behind it.
sudo cloudflared service install "$TUNNEL_TOKEN"
sudo systemctl enable --now cloudflared
journalctl -u cloudflared -f
# run once as Administrator
cloudflared.exe service install $env:TUNNEL_TOKEN
Get-Service cloudflared
# keep the machine awake while serving:
powercfg /change standby-timeout-ac 0
The same JSON this page is reading — proof the request made the full round trip.
curl -s https://chokita.com/api/status | jq '{ok, port, this_request_via_cloudflare, this_request_edge_ip}'
This panel polls the process on port 19000 every three seconds and prints what it reports about itself. No mock data — if the tunnel or the server dies, this goes red.
The honest version. This pattern is excellent, not magic.