Contents

Understanding Cloudflare Tunnels

Safely Exposing Services Without Opening Ports

Learning Cloudflare Tunnels: Safely Exposing Services Without Opening Ports

As a backend engineer, I’ve spent a long time dealing with the “classic” way of exposing services to the internet: opening ports, configuring firewalls, managing TLS certificates, and hoping nothing breaks or gets exploited.

Recently, I decided to properly understand Cloudflare Tunnels, a concept I had heard about often but never deeply explored. What started as curiosity quickly turned into a “why didn’t I learn this earlier?” moment.

This post documents my learning journey from zero intuition to a clear mental model written for anyone who wants to understand Cloudflare Tunnels without a deep networking background.

The Traditional Way

Let’s start with a very common setup.

You have:

  • A web app running on your laptop or a private server
  • It’s accessible at http://localhost:8000
  • You want to access it from the internet

Traditionally, this means:

    Internet
       |
       | (open port 8000)
       v
┌─────────────────┐
│ Router/Firewall │
└─────────────────┘
       |
       v
┌─────────────────┐
│   Your Server   │
│  localhost:8000 │
└─────────────────┘

This approach has problems:

  • You must open ports (security risk)
  • Your public IP is exposed
  • TLS certificates must be managed manually
  • Many networks (home ISPs, offices) block inbound traffic

First Intuition: What Is a Cloudflare Tunnel?

The key idea that changed everything for me:

You never expose your server to the internet. Your server connects outward to Cloudflare instead.

You run a lightweight agent called cloudflared on your machine. That agent establishes a secure outbound connection to Cloudflare.

Since outbound connections are almost always allowed:

  • No ports need to be opened
  • NAT and firewalls stop being a problem
  • Your origin server stays private

The Mental Model That Made It Click

Here’s the analogy that finally made it intuitive:

  • Your app lives inside a locked house
  • The internet is outside
  • Cloudflare is a guarded checkpoint

Instead of opening your door to everyone:

  • Your app walks out and establishes a private tunnel to Cloudflare
  • Visitors talk only to Cloudflare
  • Cloudflare forwards approved traffic through the tunnel

Diagram: Traditional vs Cloudflare Tunnel

Traditional (Port Forwarding)

Internet Users
     |
     | (public IP + open port)
     v
┌─────────────────┐
│   Your Server   │ <- Directly exposed
│  localhost:8000 │
└─────────────────┘

With Cloudflare Tunnel

Internet Users
     |
     v
┌─────────────────┐
│ Cloudflare Edge │ <- Public-facing
└─────────────────┘
     |
     | (encrypted tunnel)
     v
┌─────────────────┐
│   cloudflared   │
│     daemon      │
└─────────────────┘
     |
     v
┌─────────────────┐
│   Local App     │ <- Private
│  localhost:8000 │
└─────────────────┘

Your server never directly faces the internet.

How Traffic Flows

  1. A user visits https://myapp.ak.com
  2. DNS routes the request to Cloudflare
  3. Cloudflare handles HTTPS and security checks
  4. Cloudflare forwards the request through the tunnel
  5. Your local app receives it on localhost:8000
  6. The response travels back the same way

All traffic is encrypted end-to-end.

Setting Up a Cloudflare Tunnel

Below is the minimum set of commands needed to get a tunnel running.

1. Install cloudflared

macOS

brew install cloudflared

Linux (Debian/Ubuntu)

curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb \
  -o cloudflared.deb
sudo dpkg -i cloudflared.deb

2. Authenticate with Cloudflare

cloudflared tunnel login

This opens a browser and asks you to log in and select a domain. Cloudflare now trusts this machine.

3. Create a Tunnel

cloudflared tunnel create my-tunnel

This creates:

  • A tunnel ID
  • A credentials JSON file on your machine

4. Configure the Tunnel

Create ~/.cloudflared/config.yml:

tunnel: my-tunnel
credentials-file: /Users/amish/.cloudflared/<tunnel-id>.json

ingress:
  - hostname: myapp.ak.com
    service: http://localhost:8000
  - service: http_status:404

This means:

  • Requests to myapp.ak.com go to localhost:8000
  • Everything else returns 404

5. Route DNS to the Tunnel

cloudflared tunnel route dns my-tunnel myapp.ak.com

Cloudflare automatically creates the DNS record.

6. Run the Tunnel

cloudflared tunnel run my-tunnel

Now your local app is live at: https://myapp.ak.com

No open ports. No public IP exposure.

What Problems This Solves Instantly

  • No port forwarding
  • No public IP exposure
  • No manual TLS setup
  • Automatic HTTPS
  • DDoS protection
  • Works behind NAT, CGNAT, hotel Wi-Fi

Common Use Cases

Cloudflare Tunnels are perfect for:

  • Local development demos
  • Internal dashboards
  • Home-lab services
  • Receiving webhooks on localhost
  • Temporary or experimental apps

Anything that should be reachable but not exposed.

How This Changed My Mental Model

Before learning Cloudflare Tunnels, I believed:

“To expose a service, I must open a port.”

After learning this, my default became:

“I should avoid opening ports unless there’s no alternative.”

That’s a meaningful shift in how I think about security and infrastructure.

Final Takeaway

Cloudflare Tunnels feel like a modern default:

  • Secure by design
  • Simple to reason about
  • Friendly for developers
  • Powerful enough for production use