Invisible Bridge — Exposing Our Home Blog Externally with Cloudflare Tunnel

Written by

in

Self-hosting build guide, Part 9. After buying a domain, how do we pull traffic all the way to our home computer?

← Previous: Part 8 — Bots Arrive in 24 Hours (Security P0)
→ Next: Part 10 — What Not to Publish (Privacy Review)

TL;DR

  • Problem: WordPress runs on our home mini-PC, but how do we connect the purchased domain (sticknstone.org) to it from outside?
  • Old way: Open a router port → register public-IP DNS → renew SSL. Five headaches.
  • New way: Cloudflare Tunnel — the mini-PC pre-opens an outbound bridge to Cloudflare. No router hole, no IP exposure.
  • Tool: Run the cloudflared daemon with Docker. Five minutes.
  • Free bonus: WAF, DDoS protection, CDN, SSL, traffic stats — all free.
  • Cost: $0 (domain renewal ~$10/yr separate).

1. The Problem to Solve

The blog (WordPress) is hosted on our home mini-PC. The domain (sticknstone.org) is bought. So how do the two meet?

The ordinary path (port forwarding): five headaches

Headache Why
Router hole Opening port 443 invites bot attacks forever
Shared IP The ISP keeps changing it; needs an auto-renew script
Location exposure The IP alone hints at your rough neighborhood
SSL renewal Let’s Encrypt by hand every 90 days
Traffic surge A DDoS can paralyze your home internet

2. Another Path: Build a Bridge

A shift in thinking: don’t accept connections coming in from outside. Instead, pre-create an outgoing connection from home to somewhere external.

Request path: port forwarding vs. Cloudflare Tunnel Top: the old way. The visitor comes straight in through open router port 443 to the mini-PC public IP, and a hacker can attack the same port. Bottom: the new way. The visitor goes to Cloudflare Seoul DC, which hands the request to cloudflared through an outbound channel the mini-PC opened in advance. The hacker is blocked at Cloudflare. Old way — Port Forwarding Visitor Open router port 443 Mini-PC public IP WordPress inbound from outside open exposed Hacker attack New way — Cloudflare Tunnel Visitor Cloudflare Seoul DC Mini-PC cloudflared WordPress pre-opened outbound channel (mini-PC dials out first) Hacker blocked
Figure 1. Request path with port forwarding (top) vs. Cloudflare Tunnel (bottom) — the tunnel opens no router port

The key: the mini-PC reaches out first. Instead of outside → home, keep an always-open outbound channel home → Cloudflare. Visitor requests are forwarded by Cloudflare through that channel to the mini-PC.

Hotel analogy

A hotel guest (the mini-PC) wants to meet a friend.

  • Ordinary path: Post your room number at the entrance. The friend walks in by that number — and so does the thief.
  • Bridge path: Leave a note with the front desk (Cloudflare): connect anyone asking for my friend to my room. The friend just gives a name; the desk routes them. The room number is never exposed, and the desk screens suspicious visitors first.

3. Traffic Flow — When a Visitor Types the Domain

Traffic flow when a visitor types the domain The visitor's browser sends GET to sticknstone.org. Cloudflare Seoul DC applies a first WAF/DDoS filter, then forwards the request through the pre-opened outbound channel to cloudflared on the mini-PC, which passes it to the WordPress container at localhost:8090. The HTML response travels back through the same channel to Cloudflare, which returns it to the visitor with SSL termination and CDN caching. Visitor browser Cloudflare Seoul DC cloudflared (mini-PC) WordPress container GET https://sticknstone.org WAF/DDoS first filter Forward via pre-opened outbound channel http://localhost:8090 HTML response Reply via outbound channel SSL termination + CDN cache + response Through the tunnel (outbound channel) Response
Figure 2. The order in which the request and response travel when a visitor types the domain

Four steps: Visitor → Cloudflare (filter) → bridge → mini-PC → reverse. The mini-PC IP never appears anywhere.


4. The Bridge Keeper: cloudflared

The small daemon that holds this outbound channel open from home is cloudflared — Cloudflare’s official open source.

Why run it with Docker

Docker is already running on the mini-PC. It’s natural for cloudflared to stay in the same hotel as the other containers.

Docker layout on the mini-PC Inside the Docker host on the mini-PC (Ubuntu 24.04) run cloudflared, wordpress-wordpress-1 (:8090), wordpress-db-1 (MariaDB), wordpress-redis-1 (Object Cache), umami_umami-1 (:3001) and umami_db-1 (Postgres). The outside Cloudflare network connects only to cloudflared through the outbound channel, and cloudflared, running with network_mode host, forwards to WordPress and Umami. Mini-PC (Ubuntu 24.04) Docker host Cloudflare network cloudflared network_mode: host wordpress-wordpress-1 :8090 umami_umami-1 :3001 wordpress-db-1 MariaDB wordpress-redis-1 Object Cache umami_db-1 Postgres outbound channel
Figure 3. Docker layout on the mini-PC — only cloudflared runs on the host network to reach WordPress and Umami

Only cloudflared runs with network_mode: host so it can reach localhost:8090 (WP) and localhost:3001 (Umami) on the host.

5-minute install guide

Folder + config files:

/home/user/cloudflared/
├── .env                # TUNNEL_TOKEN=eyJh... (token from the Cloudflare dashboard)
└── docker-compose.yml

docker-compose.yml:

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    restart: unless-stopped
    network_mode: host
    command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
    env_file: .env

Start + verify logs:

cd /home/user/cloudflared
sudo docker compose up -d
sudo docker logs cloudflared --tail 20

A successful log shows four lines (simultaneous connections to four Cloudflare Seoul data centers):

Registered tunnel connection ... location=icn06 protocol=quic
Registered tunnel connection ... location=icn01 protocol=quic
Registered tunnel connection ... location=icn05 protocol=quic
Registered tunnel connection ... location=icn06 protocol=quic

The mini-PC connects to Cloudflare over four backup channels. If one dies, the other three stay alive.


5. Tokens Are Risky — Where to Keep Them

The token cloudflared uses to authenticate with Cloudflare is shown only once at issuance.

Principles for handling the token

  • Store it in plaintext in .env, with chmod 600 (owner read/write only).
  • Never paste it in plaintext into chats, notes, or git commits.
  • If you suspect exposure, revoke and reissue from the Cloudflare dashboard immediately.

The exact commands

sudo chown user:user /home/user/cloudflared/.env
sudo chmod 600 /home/user/cloudflared/.env

6. What Comes Along for Free

Just by laying this bridge on Cloudflare’s free plan, these bonuses come along:

Bonus What it does Typical price
WAF Auto-blocks known attacks like SQL injection and XSS $20/mo~
DDoS protection Absorbs traffic surges automatically, unlimited $200/mo~
CDN Caches static files worldwide; fast for foreign visitors $10/mo~
Auto SSL Zero worry about Let’s Encrypt renewals $5/mo~
Traffic stats Graphs in the Cloudflare dashboard (analytics tools)

Total cost: $0. Only ~$10/yr for domain renewal.


7. Pitfalls — What We Actually Hit

Pitfall 1: Confusing the Operating System choice

The Cloudflare dashboard’s cloudflared install screen asks you to pick an OS. At first I picked Windows, thinking of my own Windows PC. Wrong. cloudflared installs on the mini-PC (Ubuntu), not my Windows machine. The right choice was Docker.

Pitfall 2: Pasting the token straight into LLM chat

If you send the copied token into an LLM chat, that content is stored on an external server. The token is as sensitive as a card PIN. Revoke it from the Cloudflare dashboard as soon as you’re done.

Pitfall 3: How does cloudflared forward to other services?

Just starting the cloudflared container connects it to Cloudflare, but where incoming requests go is a separate setting — the Public Hostname config. Covered in the next part (Part 10).


8. Validation

Right after startup, check Connection Status at the bottom of the Tunnel page in the Cloudflare dashboard.

Before:  No connection detected yet
After:   Connected (check)

When that one line flips, the bridge is up.


FAQ

Q. How is this different from Tailscale Funnel?
Both bypass NAT and hide the mini-PC IP. Cloudflare Tunnel also bundles WAF, DDoS, CDN, and use of your own domain for free. Tailscale Funnel forces a dedicated domain and offers no shield. For a blog, Cloudflare wins.

Q. What if the mini-PC shuts down?
The bridge drops. Visitors see a 521 (Web server is down) from Cloudflare. When the mini-PC boots again, cloudflared reconnects automatically (Docker restart: unless-stopped).

Q. Does Cloudflare see the traffic? Privacy concern?
Cloudflare terminates SSL, so it can see plaintext traffic. The trust model assumes Cloudflare is on your side. If you don’t trust it, the Tunnel isn’t a fit (alternative: your own reverse proxy + Let’s Encrypt + dynamic DNS).

Q. Limits of the free plan?
Unlimited bandwidth, unlimited sites, unlimited tunnels. Extras like Workers and R2 have separate limits. For running one blog, the free plan is enough for life.

Q. Does the domain have to be .org?
No. .com / .net / .io / .dev all work the same. Using Cloudflare DNS for the domain is just the easiest (automatic DNS registration).


Next Part Preview

Part 10 sets the Public Hostname to map sticknstone.org → WordPress, and covers what you must not write in a post (privacy review).


One-Line Summary

To run a blog on a home computer, skip the old method of punching a router hole and exposing your IP. Instead, pre-open an outbound channel from the computer to Cloudflare — that’s Cloudflare Tunnel. The little daemon that keeps the channel open is cloudflared. Five minutes with Docker.


References

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *