Cloudflare Tunnel vs Reverse Proxy: Which One Should You Use?
Cloud

Cloudflare Tunnel vs Reverse Proxy: Which One Should You Use?

One of these opens no inbound ports and works behind CGNAT. The other is simpler, portable, and doesn't ask you to trust a daemon or a vendor's uptime with every request. Neither one is the obviously correct default — the right answer depends on which failure you'd rather own.

Published September 25, 202610 min readUpdated Sep 6, 2026

Written by · Full-Stack Agentic AI Software Engineer — AI Agents, Automation & Revenue Systems for GTM/RevOps teams

In brief

Should I put a Cloudflare Tunnel or a classic reverse proxy in front of my self-hosted app?

Cloudflare Tunnel runs cloudflared on the origin, which dials outbound connections to Cloudflare's edge and never opens an inbound port at all — it works behind NAT or CGNAT, where a reverse proxy on a residential connection simply can't. The cost is a dependency on a vendor daemon staying up and configured correctly, and on Cloudflare's network being reachable at all, plus a 100-second idle timeout on WebSocket and long-lived connections that applies on the Free and Pro plans regardless of tunnel or plain proxying. A classic reverse proxy on 80/443 is simpler, has no vendor daemon in the request path, and is portable to any other CDN or no CDN at all — but it requires a public IP and inbound ports, and hiding the origin behind it takes the extra firewall step covered separately. Pick tunnel for CGNAT, home labs, and anything where opening a port isn't an option; pick a reverse proxy for anything you want to keep portable off Cloudflare later.

  • cloudflared makes outbound-only connections to Cloudflare's edge on TCP/UDP 7844 — there is no inbound port to open, forward, or firewall, which is the entire value proposition behind CGNAT
  • The 100-second idle timeout on WebSocket and long-lived HTTP connections is a Free/Pro-plan-wide Cloudflare limit, not something specific to tunnels — a plain reverse proxy behind Cloudflare's proxy hits the same ceiling
  • A tunnel outage is a dependency on cloudflared staying alive and configured, and on Cloudflare's network being reachable — a reverse proxy's only dependency is the box itself and the network path to it
  • A reverse proxy is portable to any other provider or no provider at all; a tunnel's configuration lives partly in Cloudflare's dashboard, which is a soft form of lock-in worth naming honestly
  • SSH over a tunnel eliminates the port-22-exposure question entirely, which is a real answer to a debate covered elsewhere on this site

Evidence notes

cloudflared connection mechanism

cloudflared dials four persistent QUIC connections outbound to separate Cloudflare colocation facilities on TCP/UDP port 7844 and holds them open; incoming requests are routed down whichever of those four connections the edge picks, then proxied locally by cloudflared to the origin service. No inbound port is ever opened on the origin's firewall.

WebSocket and long-lived connection timeout

Free and Pro Cloudflare plans enforce a 100-second idle timeout on WebSocket and other long-lived HTTP connections proxied through Cloudflare's network. This applies to any proxied traffic on those plans, tunnel or reverse proxy, and is commonly worked around with a client-side heartbeat every 30 seconds.

DNAT and firewall behavior a reverse proxy still has to account for

A classic reverse proxy publishing ports 80/443 still needs the origin firewalled to Cloudflare's IP ranges to actually hide the origin — the reverse-proxy path does not include that step automatically, unlike a tunnel, where the origin never has an inbound listener to hide in the first place.

Cloudflare Tunnel pricing, checked September 2026

On 15 July 2026, Cloudflare made Cloudflare Tunnel free for every organization outright, ending the bandwidth-based pricing that previously applied at scale. There is no per-GB charge for tunnel traffic; costs, where they exist, come from Zero Trust seat counts rather than the tunnel itself.

"Just use a Cloudflare Tunnel" gets said in every self-hosting thread the moment someone mentions they're behind CGNAT or don't want to touch their router's port forwarding. It's often the right answer. It's not automatically the right answer, and the two options genuinely trade off against each other in ways that matter once you're running something you plan to keep running for years, not a weekend project. Both routes end with a working domain pointed at a self-hosted app; they get there through completely different mechanisms, with different failure modes attached to each.

What is a Cloudflare Tunnel actually doing on the wire?

cloudflared, a small daemon you run on the origin, dials four persistent QUIC connections outbound to four separate Cloudflare colocation facilities on TCP/UDP port 7844 and holds them open. When a request for your hostname arrives at Cloudflare's edge, the edge picks one of those four connections, multiplexes the request onto it, and cloudflared receives it and proxies it locally — to localhost:3000, to a container's internal address, to whatever your ingress rules point at. At no point does anything initiate a connection into your network from the outside. The firewall on the origin never sees an unsolicited inbound packet on any port, because there's no listening socket for one to arrive at. A lot of what makes Cloudflare tunnel vs reverse proxy work in practice is process rather than code, which is the territory the team at XenGrowth covers.

That's the entire value proposition, and it's a real one: this setup works identically whether the origin has a public IP, a CGNAT address shared across hundreds of other customers on the same ISP, or no routable address at all. Port forwarding, a static IP, a firewall rule opening 443 — none of it applies, because the connection direction is reversed from where a reverse proxy needs it to be. A scanner sweeping the internet for open ports on your address range finds nothing to connect to, not because a firewall is blocking it, but because there's genuinely no listening socket there to find.

What does a classic reverse proxy actually cost you that a tunnel doesn't?

A public IP and two open inbound ports, at minimum. Traefik or nginx listening on 80 and 443, terminating or passing through TLS, and forwarding to whatever's behind it — this is the setup most self-hosting guides assume by default, and it requires exactly what a tunnel is built to avoid: something has to be reachable from the internet for a connection to land on it. If you're on a residential connection behind CGNAT, this option is closed before you even start, not because it's hard to configure but because there's no public address on your side of the NAT for anyone to connect to. And even once it's up, a reverse proxy alone doesn't hide the origin — that's the firewall-to-Cloudflare's-IP-ranges step covered in how I use Cloudflare to hide my origin server, which a tunnel makes unnecessary simply because there's no inbound listener on the origin to hide in the first place. The XenGrowth resource library works through the operations side of this in more operational detail.

What does a tunnel cost you that a reverse proxy doesn't?

Two dependencies you don't otherwise have. First, cloudflared has to actually be running and correctly configured — it's a daemon, it can crash, it can fall behind on updates, and if it's down, your app is unreachable regardless of whether the app itself is perfectly healthy, because every request has to pass through it. A reverse proxy has the same failure mode in principle, but it's a much simpler piece of software doing one job it's done for decades, versus a newer vendor-specific daemon maintaining outbound connections to a specific provider's edge.

Second, and this is the one people underweight: your app's reachability is now tied to Cloudflare's network being up, full stop, with no way to route around it even temporarily. A reverse proxy behind Cloudflare's DNS can, in a pinch, be reached by temporarily un-proxying the DNS record and hitting the origin directly (assuming it isn't firewalled off, or using the firewall's own allowlist as a controlled exception) — an emergency escape hatch that a tunnel doesn't have, because the tunnel's daemon is Cloudflare's product, not just Cloudflare's DNS in front of your own listener. That's a soft form of lock-in worth naming honestly: a reverse-proxy setup can drop Cloudflare and point DNS elsewhere in minutes; a tunnel setup has more of its configuration living inside Cloudflare's own dashboard and product surface.

Does a tunnel handle WebSockets and long requests worse than a reverse proxy?

Not specifically — and this is a common misconception worth correcting directly. The 100-second idle timeout on WebSocket and other long-lived connections is a property of Cloudflare's Free and Pro proxy layer itself, not something a tunnel adds on top. A reverse proxy sitting behind Cloudflare's orange cloud on the same plan hits the identical ceiling, because the timeout is enforced at Cloudflare's edge before the request ever reaches your origin, tunnel or not. The practical fix is the same either way: a client-side heartbeat every 30 seconds or so keeps a WebSocket connection from ever going idle long enough to trip the limit. Where the two setups genuinely differ on latency and connection behavior is smaller and more boring — a tunnel adds the hop through cloudflared itself, and its four-connection multiplexing model behaves slightly differently under a burst of simultaneous long requests than a proxy holding its own listener open directly. Neither difference is large enough to be the deciding factor for a typical self-hosted app. XenGrowth on governed AI marketing workflows approaches this from the AI agents and marketing automation side.

Does a tunnel cost anything, or hit a bandwidth limit?

As of July 2026, no — Cloudflare made Tunnel free for every account outright, dropping the bandwidth-based pricing that used to apply at real scale. There's no per-GB charge and no tier where tunneling itself starts costing money; the cost, if any ever shows up, comes from Zero Trust seats once a team exceeds the free plan's user cap, which is an access-control concern rather than anything to do with the tunnel's data path. For a single self-hosted VPS this removes what used to be a legitimate hesitation about tunnels — cost is no longer part of the tunnel-versus-proxy decision at all, and the choice comes down entirely to the tradeoffs above. It's worth saying plainly that this wasn't always the case, and a comparison written even a year earlier would have had to weight a real cost difference into the decision that simply isn't there anymore.

Situation

Recommendation

Why

Behind CGNAT or a residential connection with no static IP

Tunnel

There's no public address for a reverse proxy to be reached at, full stop

VPS with a static IP, want to keep the option of leaving Cloudflare later

Reverse proxy

DNS repoint and a firewall change is close to the entire migration; a tunnel's config lives partly in Cloudflare's product

Want SSH reachable with zero open inbound ports

Tunnel, for SSH specifically

Removes the port-22 exposure question entirely rather than just reducing its noise

Running a latency-sensitive service and comfortable managing router/firewall config

Reverse proxy, direct

One fewer hop than routing through cloudflared

Single small self-hosted app, static IP, no strong opinion either way

Reverse proxy plus the origin-firewall step

Fewer moving parts outside your own box, and no dependency on a vendor daemon staying current

Dimension

Cloudflare Tunnel

Reverse Proxy (Traefik/nginx)

Works behind CGNAT / no public IP

Yes — this is the whole point

No — requires a routable public IP

Inbound ports required

None

80 and 443, minimum

Origin-hiding by default

Yes — no inbound listener to find

No — needs a separate firewall-to-Cloudflare-IPs step

Vendor daemon dependency

cloudflared must stay running and configured

None — the proxy is standard, swappable software

Portability off Cloudflare

Lower — tunnel config lives partly in Cloudflare's product

Higher — DNS repoint is close to the whole migration

100s idle-connection timeout (Free/Pro)

Applies

Applies identically — this is a plan limit, not a tunnel limit

Setup complexity

One daemon, one config file, no router changes

Standard, well-documented, but router/firewall work if behind NAT

Can a tunnel expose more than just HTTP?

Yes, and this is a genuine capability gap in the reverse proxy's favor of nothing at all — a tunnel can carry raw TCP as well as HTTP, which a plain reverse proxy on 80/443 was never built to do for something like a database or an internal admin protocol. Routing SSH through cloudflared, as covered above, is one instance of this. The broader version is private network routing: cloudflared can advertise an entire internal CIDR range to Cloudflare's Zero Trust network, and a client running the WARP agent can then reach services on that private network — a database, an internal dashboard — without any of them ever being published on a public port, on either end. A reverse proxy has no equivalent to this; the closest analogue is a real VPN, which is its own separate piece of infrastructure to run and maintain rather than something the same tunnel already gives you. XenGrowth on building one SEO and GEO content system works through AI search, GEO and discovery in more operational detail.

So which one should you actually run?

Tunnel, without much hesitation, if you're behind CGNAT, on a residential connection, or running anything — a home lab, a personal project on a spare machine — where opening a port simply isn't an option or isn't something you want to depend on your ISP or router for. It's also the better default for SSH access specifically: routing SSH through a tunnel means port 22 is never open to the internet at all, which is a cleaner answer than anything in the port-22 debate — there's no port to scan, moved or otherwise, because there's no listener.

  1. Behind CGNAT, no static IP, or on a connection you don't control the router on: Tunnel. There's no realistic alternative.

  2. Running a VPS with a static public IP and want to preserve the option of leaving Cloudflare later without re-architecting anything: reverse proxy, firewalled to Cloudflare's IP ranges.

  3. Want SSH reachable without any open inbound port, on a box that otherwise runs a reverse proxy for HTTP: run both — tunnel for SSH access specifically, reverse proxy for the app.

  4. Running something latency-sensitive at a scale where every extra hop matters and you're comfortable managing router-level port forwarding: reverse proxy, direct.

  5. Uncertain, running a single small self-hosted app, no CGNAT constraint either way: reverse proxy plus the origin-firewall step is the more portable default, precisely because it depends on fewer moving parts outside your own box.

A tunnel trades an open port for a dependency on a daemon and a vendor. A reverse proxy trades that dependency back for a port you have to firewall yourself. Neither trade is free — the question is only which failure you'd rather be the one debugging at 2am.

The honest version of this comparison isn't "tunnels are the modern choice and reverse proxies are legacy." A VPS with a static IP has always had the option of the simpler, more portable setup, and CGNAT is the actual reason tunnels exist as a category at all — solving a real constraint, not replacing a working pattern with a fancier one. Pick based on which constraint you actually have, not which one gets recommended more often in a thread that usually isn't specifying whether the person asking even has a public IP to work with, let alone whether they'd rather debug a daemon or a firewall rule at 2am.

Further reading from XenGrowth

Where this work meets go-to-market

the XenGrowth practice writes for the teams who have to run Cloudflare tunnel vs reverse proxy day to day.

Tunnel or reverse proxy?

Four questions about your network and your constraints. The two approaches solve overlapping problems in genuinely different ways, and one of them is usually clearly right for a given setup.

1 / 4
Can you accept inbound connections on ports 80 and 443?

Home connections behind CGNAT, and some corporate networks, cannot.

Apply this article

How to turn insights into execution

A practical sequence for teams turning concepts into production outcomes.

CloudflareCloudflare TunnelReverse ProxycloudflaredSelf-HostingNetworkingVPS Securitycloud

Audit your current state

Map the bottlenecks and constraints connected to the article’s core problem.

Choose one bounded change

Test the most useful recommendation on one workflow before widening the scope.

Measure what changed

Keep the parts that improve the work, document what failed, and make the next decision from evidence.

Next step

Need help applying this in your stack?

I can translate these patterns into a concrete implementation plan for your team.

Discuss implementationBack to blog

Replies usually within 24 hours.

Next Steps

Continue reading

What Cloudflare's Free Tier Actually Does for a Self-Hosted App

Cloudflare's free plan gets recommended for every self-hosted setup, usually without anyone saying what it doesn't cover. Here's what $0 genuinely buys a one-person VPS, checked against 2026 pricing, and exactly where it stops.

Navigate

How I Use Cloudflare to Hide My Origin Server

Turning on the orange cloud doesn't hide anything by itself. It hides your origin only if you also make the origin refuse to talk to anyone who isn't Cloudflare — and there are at least four ordinary ways your real IP gets out anyway if you skip that part.

Navigate

My Complete Self-Hosted Stack for SaaS in 2026

Contabo, Coolify, Docker, Cloudflare, Postgres, R2, Resend, Uptime Kuma, Turborepo and Docker Hub. Here's every piece of the stack I actually run, what each one replaced, and why I picked it over the alternatives.

Navigate

How I Secure a Fresh VPS Before Deploying Anything

A brand-new VPS gets scanned within minutes of getting an IP address. Here's the exact order I run through before a single container touches the box — and which of these steps are real protection versus which ones are just theatre.

Navigate

Don't Self-Host Until You Understand These 7 Things

This isn't a gate to keep you out. It's a readiness check — seven things worth being honest with yourself about before you're the one holding the pager, because a managed platform is still the right call for a lot of people right now.

Navigate

Preview Environments Without Vercel: Branch Deploys on Your Own VPS

A preview URL per branch is the one Vercel feature people miss most after leaving. It's buildable on your own server, and it's genuinely harder than Vercel makes it look — mostly because of the database, which nobody's marketing page mentions.

Navigate

What Cloudflare's Cache Actually Does for Speed and Bandwidth

Cache HIT and MISS aren't a minor speed difference — they're two different request paths entirely, one of which never reaches your server. Here's the mechanism, the free tier's real limits, and exactly how to check your own hit ratio instead of trusting a number nobody measured.

Navigate

The Contabo + Coolify + Cloudflare Stack That Replaced My Vercel Bill

Three pieces, not thirty. Here's exactly what each one does, why that specific combination and not one of the dozen others I considered, and which parts of Vercel each one is actually standing in for.

Navigate

SSH Hardening: Keys, Brute-Force Protection and What Fail2ban Still Buys You

Password auth off, root login off — that's most of the story, and I've already told it. This is the rest: which key type actually matters, the PAM gotcha that quietly undoes 'disable password auth,' and an honest answer on what fail2ban is even for once there's no password left to brute-force.

Navigate

Why Next.js Feels Slow on a VPS — and the Six Things That Fix It

The app that felt instant on Vercel goes home to a cheap VPS and forgets how to load fast. The framework didn't change. Six specific things underneath it did, and none of them are next/image.

Navigate