a gentle introduction to cloudflare tunnels
CloudFlare Tunnel allows you to expose services within a network to the public without a publicly routable IP address. For example, you can expose a website to the public under a public hostname, without exposing your machine’s IP address. You don’t even need to deal with SSL for HTTPS, as this is automatically handled by CloudFlare! This can be beneficial because, for one, exposing your machine directly to the Internet poses a potential security risk; for another, it might not be feasible to do so. By exposing your services with Tunnel, you receive all the benefits of using CloudFlare network, including DDoS protection, caching, and more, without exposing your machines to the public.
CloudFlare Tunnels support many protocols, including HTTP/S, TCP, UDP, and even SSH. This unlocks many use cases beyond exposing HTTP services. As an example, you can expose a game server, such as a Minecraft server, over TCP, under a publicly accessible hostname.
How does CloudFlare Tunnel work?
CloudFlare Tunnel relies on a persistent connector called cloudflared
that establishes an encrypted connection between your machine and the global CloudFlare network. Each connector is bound to a tunnel, which maps a public hostname to an address the connector can access. As an example, code.nym.sh, my public Gitea instance, is mapped to http://localhost:3000
. Since the associated cloudflared
is running on the same host, it can access localhost
, and therefore any locally running service, including Gitea.
Suppose a service is exposed under service-a.com
which is publicly accessible, and an HTTP request is made to it. The request will go through the following steps:
- It is routed to CloudFlare’s network
- Based on the hostname, it is then forwarded to the corresponding tunnel.
- The request is then forwarded to the appropriate connector, which then forwards it to the configured (local) address.
This is a high-level overview of CloudFlare tunnel’s architecture. If you are interested in the details of Tunnel, including how it handles redundancy and failover, check out this official reference document.
Deploying cloudflared
There are different ways you can deploy cloudflared
. I have tried the following approaches:
- Running
cloudflared
on the same host where the services are running (this is my setup currently) - Running
cloudflared
on a separate machine in the same network (for example, within the same Tailnet) where the services are running
In the first scenario, an instance of cloudflared
is deployed on the machine that is running the service you wish to expose. In my case, I have 2 machines running different services I need to expose, so I deployed 2 instances of cloudflared
, each responsible for exposing the services running on the same machine.
In the second scenario, an instance of cloudflared
is deployed on a dedicated machine that acts as the “gateway” to services running in your network. This can look like one machine running cloudflared
in a Tailnet that is responsible for the tunneling of services running in the same network, potentially on different machines.
There are no right or wrong approaches. I am providing these scenarios as reference points, but use your best judgment to adapt them to your needs.
Configuring CloudFlare Tunnel
Before creating a Tunnel, make sure that the domains you wish to use is added to CloudFlare (guide here).
Creating a Tunnel is straightforward. First, create a CloudFlare Tunnel in the Zero Trust CloudFlare dashboard. CloudFlare should then provide instructions on how to install and run cloudflared
for different environments and operating systems.
Once the connector is online, add a public hostname to the tunnel. You can map a root domain name, subdomains, and even subpaths. Then, specify the type and the local address for the service that you want to expose. For example, if you want to expose your backend server running on localhost:8000
under the URL https://api.acme.com
, do the following:
- Specify
api
as subdomain - Specify
acme.com
as domain - Specify the service type as
HTTP
(notHTTPS
, as this is from the perspective of the connector) - Specify the URL as
localhost:8000
Save the settings, and voila! You have exposed your backend to the public Internet! How easy is that?
Final Words
As always, feel free to shoot me an email or DM me on X if you have any questions about CloudFlare Tunnels.