RouteHardenHire us
← Back to blog
Self-Hosted Privacy··8 min read

Self-hosting behind Cloudflare Tunnel without a public port

How to use Cloudflare Tunnel for published apps and private-network routes, when to use Access, and where Tunnel stops being the right tool.

First choose the right Tunnel mode.

Most Cloudflare Tunnel confusion comes from pretending there is only one use case. There is not.

For self-hosters, "Cloudflare Tunnel" usually means one of two different patterns:

  1. publish one app or hostname to the internet
  2. expose private IPs or private hostnames to enrolled users and devices

If you mix those up, the rest of the design gets muddy fast.

Use the split this way:

ModeWhat it exposesWhat clients needBest for
Published appone hostname to one serviceordinary app client, often with Access in frontdashboards, web apps, controlled inbound exposure
Private routeIPs, CIDRs, or private hostnamesenrolled Cloudflare One Client devicesremote access to internal resources

If you are unsure which column your use case belongs in, ask a simpler question: should an unauthenticated browser on the public internet even be able to discover this hostname? If yes, you are probably in published-app land. If no, and the resource should feel internal, private-network mode is usually the better fit.

What Tunnel is good at

Cloudflare's own Tunnel overview captures the core attraction well: no public IP requirement, outbound-only connector, no open inbound port requirement on the origin.

That is why self-hosters like it.

It gives you a way to place an application or route behind Cloudflare's edge without directly opening the origin host to the internet in the usual way.

The security upside is straightforward:

  • outbound-only connector
  • no ordinary public listen socket for the app
  • simpler positive-security firewall posture

It is a good tool when the problem is controlled inbound access.

Pattern 1: publish one self-hosted app

The published-app mode is the one most people mean first. You map a public hostname to a local service behind cloudflared.

Cloudflare's routing docs support service types including HTTP, HTTPS, SSH, TCP, RDP, SMB, bastion, and even a fixed-response http_status:404.

The clean operator pattern is:

  • explicit hostname mapping
  • explicit local target
  • explicit catch-all

The value of that pattern is not just neatness. It means you can answer "what exactly is this tunnel supposed to expose?" without reading a novel.

For example:

cloudflared tunnel route dns <TUNNEL_NAME> app.example.com

With ingress like:

ingress:
  - hostname: app.example.com
    service: http://127.0.0.1:8080
  - service: http_status:404

That last http_status:404 rule matters because it gives you a clean catch-all instead of letting unmatched traffic wander into undefined behavior.

If the app is sensitive, this is also where Cloudflare Access belongs. Tunnel gets traffic to the app; Access can add identity gates in front of it. See Cloudflare's docs on self-hosted applications.

That distinction is worth keeping sharp. Tunnel is connectivity plumbing. Access is who-gets-in policy. People often talk as if the first secretly includes the second. It does not.

Pattern 2: private-network routes

The second mode is different enough that it should be taught as a different product shape, even though the connector is still cloudflared.

Cloudflare's private network docs cover exposing HTTP and non-HTTP private resources to enrolled users and devices, usually through the Cloudflare One Client.

This is not "public hostname reverse proxy, but more private."

It is closer to remote-access plumbing:

  • tunnel route for an IP or CIDR
  • enrolled client devices
  • user and device policy
  • split-tunnel awareness on endpoints

That endpoint piece matters more than many self-hosters expect. Private-network mode is not only an origin project. If enrolled devices do not carry the right client routing behavior, the tunnel looks broken when the real issue is endpoint policy.

Cloudflare's Connect an IP/CIDR guide also notes something people miss constantly: WARP excludes RFC 1918 traffic by default, so split-tunnel settings may need adjustment if your private routes overlap ordinary private address space.

That is the sort of operational footnote that becomes the whole bug ticket if you ignore it.

Outbound-only does not mean bidirectional magic

Cloudflare's private-network docs are also explicit that cloudflared only proxies user-initiated traffic to servers. Server-initiated connections still use the host's normal routing table.

That is important enough to say twice.

Tunnel is not a magical full-mesh overlay where every packet now obeys Cloudflare simply because a connector is running. It is a controlled path for specific traffic patterns.

This is also where some designs stop fitting Tunnel. If you need broad site-to-site behavior, symmetric server-initiated connectivity, or something that behaves like a general private overlay, you may be in a different tool category entirely.

That is not a criticism of Tunnel. It is the value of naming the product boundary correctly. A tool aimed at controlled inbound connectivity should not be judged by whether it impersonates every other private-network shape on the market.

If you need general site-to-site semantics or full private routing behavior, you may be in a different problem space entirely. That is where /blog/self-hosted-wireguard-2026 may be the more honest tool.

The firewall story is one of the best parts

Cloudflare's configuration docs say cloudflared connects outbound on port 7844, and they recommend blocking all ingress traffic to the origin for a positive security model.

That is one of the strongest reasons to use Tunnel.

The boring firewall rule box is:

Allow egress to Cloudflare on port 7844.
Block direct inbound access to the origin host.

That does not make the app invincible. It does remove a whole class of "why is the origin directly reachable?" mistakes.

For self-hosters, that is a serious win. Too many origin exposures come from ports that were "temporarily" opened and never cleaned up. Tunnel makes block-all-ingress feel like the default instead of the aspirational cleanup step.

Cloudflare also documents that the tunnel establishes four outbound-only encrypted connections to at least two data centers. That is useful operational context because it explains why one connector can still have some resilience without you manually inventing a cluster story.

What Tunnel does not do

This section is the one most marketing-adjacent explainers skip.

Tunnel does not automatically give you:

  • general-purpose private mesh networking
  • a replacement for every VPN design
  • automatic server-initiated return-path control
  • protection from bad local service hygiene
  • a reason to stop caring about app-layer auth and exposure

Tunnel is great when you want controlled inbound connectivity without opening a public port. It is not the answer to every private-networking problem just because the connector model is elegant.

This is why it pairs well with /blog/network-opsec-checklist. Removing one exposed port is good. Understanding the whole access story is better.

Replicas, catch-alls, and hygiene

Cloudflare's configuration and routing docs make it pretty clear what good hygiene looks like:

  • explicit ingress rules
  • catch-all fixed response
  • avoid accidental wildcard exposure
  • use multiple connectors when resilience actually matters

Multiple connectors can help availability. They do not replace access design. A highly available misconfiguration is still a misconfiguration.

Likewise, Access policies are valuable for published applications, but they are not a substitute for knowing whether the thing you are publishing should be public, private, or not published at all.

If you cannot answer that last question clearly, stop before you write YAML. Uncertainty about the exposure model is usually the real bug.

Replicas are the same story in miniature. Multiple connectors are useful when you understand the intended exposure and want better availability for it. They are not a cure for vague architecture, and they are not a reason to skip origin hygiene.

The RouteHarden opinion

Tunnel is excellent when the problem is "I want controlled inbound access without opening a public port on the origin." That is the sweet spot.

Use published-app mode when the shape is:

  • one app
  • one hostname
  • edge identity and policy make sense

Use private-network mode when the shape is:

  • private IP or CIDR access
  • enrolled users and devices
  • client-side route policy

And stop when the problem becomes "I actually need a different private networking model."

Tunnel is a very good tool. It just gets overextended because the connector story feels so pleasant that people start handing it jobs it was never meant to do.

The best Tunnel deployments stay modest: explicit ingress, blocked origin ingress, Access where identity matters, and a clear line between published apps and private routes.

That modesty is a strength. It means when something breaks, you can usually answer whether the problem belongs to hostname routing, Access policy, client enrollment, split-tunnel behavior, or the origin itself. Systems that try to be magical are much worse at telling you where they hurt.

If I were reviewing a self-hoster's Tunnel setup, the first questions I would ask are not about YAML syntax. They would be: what exactly is exposed, who should reach it, what port 7844 egress is allowed, and what happens if the origin is contacted directly. The answers to those are the real design.