Cloudflare Tunnel: Expose services to the internet without compromising your firewall

November 3, 2021

Note: Since this guide was written, Cloudflare added GUI tunnel management. See their blog post, Ridiculously easy to use Tunnels, for more info.


Let's say you have a website running locally that you want to make publicly accessible. Pretty straightforward, right? Open up ports 80 / 443, maybe set up a reverse proxy, and add an A record pointing to your IP. Job done. Your site is online. Unfortunately, you’ve also made yourself a target by advertising your IP and opening your ports.

It will probably be fine. Keep your packages up to date, perhaps isolate the server with a hypervisor / VLAN / DMZ, and you’d have to be super unlucky to have an attacker find a way into your internal network. But traditional setups like this are difficult to set up for beginners, often require a fair bit of dedicated resources, and are time consuming to maintain.

What if you could get a website or other service online in seconds, without opening ports or worrying about inbound traffic at all, without configuring SSL, and without sacrificing performance? That’s exactly what Cloudflare Tunnel does. We use it for many of our websites, which allows us to close all ports on our servers and prevent direct attacks on our IPs.

If you’re interested in trying it out, just follow along below and you’ll be up and running in no time. This guide assumes your domain is using Cloudflare nameservers and is configurable through the Cloudflare dashboard.

1. Install the cloudflared daemon

Linux / Windows

Debian / Ubuntu install commands below. If you’re on a different system, check the downloads page for the correct install file or build from source.

# Debian / Ubuntu amd64
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb && dpkg -i cloudflared-linux-amd64.deb

# Debian / Ubuntu arm64
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64.deb && dpkg -i cloudflared-linux-arm64.deb

macOS

brew install cloudflare/cloudflare/cloudflared

Docker

While there is a docker image, we don’t recommend going this route. You then need to create a new container for every running service, instead of just controlling ingress options in a single configuration file.

2. Authenticate with your Cloudflare account

This command will generate a link which will take you to your cloudflare account. Then select one of your domains. Don’t worry about selecting all of them as the tunnel will work across multiple domains regardless.

cloudflared tunnel login

3. Create a tunnel

While it’s possible to run multiple tunnels for multiple services, one tunnel is really all you need. In this example we’ll name our tunnel main.

cloudflared tunnel create main

4. Create a configuration file

You should now have a .cloudflared directory in your home folder with two files: cert.pem and <UUID>.json. Make note of the generated UUID and create a configuration file in the same folder called config.yml. This is where your ingress rules are managed.

tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json

ingress:
  # Rules map traffic from a hostname to a local service:
  - hostname: example.com
    service: http://localhost:8000
  # Rules can match the request's path to a regular expression:
  - hostname: static.example.com
    path: /*.(jpg|png|css|js)
    service: http://localhost:8001
  # Rules can match the request's hostname to a wildcard character:
  - hostname: "*.example.com"
    service: http://localhost:8002
  # If your service uses self-signed SSL, set noTLSVerify
  - hostname: secure.example.net
    service: https://localhost:8003
    originRequest:
      noTLSVerify: true
  # Last line is a catch-all rule:
  - service: http_status:404

Replace the UUID in the example with your own, as well as the /root folder if you’re not running as the root user. There are other supported protocols and more configuration options, which you can find in the official documentation. When you’re done, validate your rules with cloudflared tunnel ingress validate.

5. Run as a service

This is optional. You can start and stop tunnels manually if you want to share something temporarily, but for anything more permanent you should run as a service.

When you install the service, your configuration file in ~/.cloudflared/ should be copied to /etc/cloudflared/. In my experience that isn’t always the case (possibly due to using a .yaml extension instead of .yml), but if your settings don’t pick up, check the file and symlink your home folder version to the /etc/cloudflared/ directory.

Linux

# Install service
cloudflared service install

# Start service
systemctl start cloudflared

# Enable at startup
systemctl enable cloudflared

macOS

cloudflared service install

Windows

See official documentation.

6. Route domain to tunnel

Add a CNAME record to point traffic to your tunnel. Recalling that we named our tunnel main, the command for this is:

cloudflared tunnel route dns main <hostname>

If you didn’t choose the zone you’re trying to create a record for when you ran cloudflared tunnel login, this may try to create a subdomain on the wrong zone. In that case, just manually enter the CNAME record pointing to <UUID>.cfargotunnel.com.

And that’s it! Your service should be on the internet and available to anyone with a connection.

To add another service, update your configuration file and restart cloudflared:

systemctl restart cloudflared

Odds & Ends

Please remember that SSH port forwarding and VPNs exist, and you don’t necessarily need to make services on remote machines public in order to use them.

While Cloudflare Tunnel can protect your machine from direct attacks, it doesn’t magically fix security flaws in your shared service. A vulnerable application is a vulnerable application.

If you need greater access control, Cloudflare Access is fairly easy to set up on services using Cloudflare Tunnel.

Why FTL?

Traditional Host
FTL

Inexpensive Monthly Plan

Our $20/site rate includes all of the features below with no long term commitment, no hidden fees, and no surprise changes. Cancel any time.

High Performance Servers

With AMD EPYC processors, RAID 10 NVMe SSDs, cutting edge software, and a 10 Gbit network connection, there's no need to limit visits, bandwidth, or files.

Fully Managed by Developers

We take care of all the technical stuff so you can focus on your website. And support is always free.

PageSpeed & SEO Optimization

As part of the migration process we work directly on your website to increase performance and ensure SEO best practices.

Reliable Daily Backups

Encrypted snapshots of your files and databases stored in the cloud, plus local redundancy via RAID 10. Our backups are not deleted after a month.

Top Notch Security

Reduce the risk of hacks and downtime with SSL, HSTS, DDoS / bot protection, containerization, secure tunneling, and site-specific Fail2ban filters.

Global Edge Caching & CDN

We leverage Cloudflare's global network of 250 edge nodes in 100+ countries, so you can reach 95% of the world's population within the blink of an eye.

Active Uptime Monitoring

If your website goes down, we'll know before you do. And if a server problem causes uptime to drop below 99.9% in any given month, you won't be charged.

Guaranteed Email Deliverability

Forget about emails not being delivered or ending up in spam folders. We'll set you up with an SMTP server for foolproof email delivery.

Smart Image Optimization

New images are scanned daily and optimized using vips. WordPress sites get our custom vips plugin to greatly improve image processing speed and quality.

Money Back Promise

If you're not satisfied with our service during the first 60 days, we'll refund all payments and help migrate your site to another provider.