Cloudflare Tunnel is a service which allows you to expose services, such as your web server, to the public internet without needing to open ports. This makes it easier to host your website inside a container with no Public IP, or even on a Dynamic IP Address. It is more secure than simply opening ports in your firewall and whitelisting the Cloudflare IP Addresses – the tunnel basically guarantees no one outside your account can access your service, thus forcing everything via Cloudflare’s WAF.
Unfortunately, Cloudflare has moved the setup of this tunnel service into their Zero Trust Dashboard, and away from the main Cloudflare Dashboard. If you aren’t the primary user on the account (account owner), you won’t have access to the Zero Trust Dashboard. This is a problem especially for agencies and freelancers who access their clients’ Cloudflare account via their own email address.
Thankfully, there’s a workaround for setting up a Cloudflare Tunnel via the API. At the time of writing, this method works even if your user account permissions don’t permit you access to the Cloudflare Zero Trust Dashboard.
In this article, I’ll walk you through the steps to create and use a Cloudflare Tunnel via the Cloudflare API.
Step 1: Generating an API Key
Before we create and configure our tunnel, you must first create an API Key from your Cloudflare Dashboard. Cloudflare has a good walkthrough on API Token creation.
- From the Cloudflare dashboard, go to My Profile > API Tokens.
- Select Create Token.
- Click on “Create Custom Token”.
- Give your token a name (e.g. Tunnel Creation).
- In the Permissions section, choose “Account”, “Cloudflare Tunnel” and “Edit”.
- In Account Resources section, select the Cloudflare account you’re going to create the Tunnel in.
- Click “Continue to summary” and then “Create Token”.
You have now created a Cloudflare API Token, which will be used to setup your Tunnel. Make a note of the Token generated on this page.
Step 2: Find your Account ID
We also need to find our Account ID before we can create our tunnel. In Cloudflare, one email address can have access to multiple Accounts. Each account generally represents an organisation. Every Tunnel is linked to one Account, and you can’t use that tunnel outside of the Account it’s been created in.
- On the Cloudflare Dashboard, go to the Account you wish to create the tunnel in, and then visit one of the Zones (Domains).
- The Account ID will be found in the bottom left corner. Make a note of this.
Step 3: Create a Tunnel
Now that we have our API Token, we can proceed to setup our tunnel. The first step is to create our Tunnel.
In these steps, we’ll be using CURL commands. CURL is a command line HTTP tool, which will allow us to run our API commands to create the tunnel. If you prefer, you are also welcome to use other HTTP tools such as Postman.
Before we can create our tunnel, we need to generate a random Base64 string. This string is used to create the secret token. Ensure you populate the below command with a secret, random string before running it – you can create your secret string via your password manager’s password generation tool. You can create your Base64 string so on MacOS and Linux with this terminal command:
echo -n 'secret string goes here' | openssl base64
Then, to create our Tunnel, run this CURL command in your terminal:
curl -k -X POST -H 'Content-Type: application/json' -H 'Authorization: Bearer <Cloudflare API Token>' -d '{
"name": "Tunnel Name",
"tunnel_secret": "<base64 secret string>"
}' 'https://api.cloudflare.com/client/v4/accounts/<account_id>/tunnels'
In this command above, we need to insert the API Token, Tunnel Name, Tunnel Secret, and our Account ID.
Running this command returns our new Tunnel ID. Make a note of this – we’ll need it in the next step.
Step 4: Configure a Tunnel
Now you have created your tunnel, you need to provide some configuration. This configuration allows the tunnel to be remotely configured, with details such as the URL of your backend service to connect to.
curl -k -X PUT -H 'Content-Type: application/json' -H 'Authorization: Bearer <Cloudflare API Token>' -d '{
"config": {
"originRequest": {
"noTLSVerify": true
},
"warp-routing": {
"enabled": false
},
"ingress": [
{
"service": "http://127.0.0.1"
}
]
}
}' 'https://api.cloudflare.com/client/v4/accounts/<account_id>/cfd_tunnel/<tunnel_id>/configurations'
In the above command, we need to insert our API Token, Backend Service URL, Account ID, and Tunnel ID.
I have found sometimes I need to save this configuration multiple times before my tunnel picks it up. You can do this by re-running this same command multiple times.
You can experiment with different configuration options by setting up a test tunnel in your own Zero Trust Dashboard, and using your Web Inspector Network Tab to view the payloads.
Step 5: Finding the Tunnel Secret
Now you have created and configured your tunnel, we can proceed to find the Tunnel Secret. This Secret allows your tunnel to connect into the Cloudflare network, join your account, and download your configuration.
curl -k -H 'Content-Type: application/json' -H 'Authorization: Bearer <Cloudflare API Token>' 'https://api.cloudflare.com/client/v4/accounts/<account_id>/cfd_tunnel/<tunnel_id>/token'
Make a note of the Tunnel Secret. You’ll need it in the next section.
Step 6: Using Cloudflare Tunnel
Cloudflare Tunnel is a binary app which can be run on a multitude of operating systems. If you were using the Zero Trust dashboard, it’d show you installation commands for multiple operating systems. I have included the setup instructions here for ease of use.
Installing and using Cloudflare Tunnel in Windows
- Download https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-386.msi.
- Run the installer.
- Open Command Prompt as Administrator.
- Run the following command:
cloudflared.exe service install <Tunnel_Secret>
Installing and Using Cloudflare Tunnel in MacOS
brew install cloudflare/cloudflare/cloudflared &&
sudo cloudflared service install <Tunnel_Secret>
Installing and Using Cloudflare Tunnel in Debian Linux
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb &&
sudo dpkg -i cloudflared.deb &&
sudo cloudflared service install <Tunnel_Secret>
Installing and Using Cloudflare Tunnel in Docker
Cloudflare Tunnel can be run in a standalone container like this:
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token <Tunnel_Secret>
If you want to include it within your own container, you may use these commands in your Dockerfile:
RUN wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -O /usr/local/bin/cloudflared
RUN chmod +x /usr/local/bin/cloudflared
Then if you use Supervisor to manage your services you can configure it like this:
[program:cloudflared]
command=/usr/local/bin/cloudflared --protocol quic tunnel run --token %(ENV_CF_TUNNEL_SECRET)s
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=true
startretries=0
You will notice above I am passing the Cloudflare Tunnel Secret as an environmental variable. This prevents us from hard-coding a secret into the Dockerfile. You can start a Docker container with an Environmental Variable like this:
docker run \
--env CF_TUNNEL_SECRET=<Tunnel_Secret> \
container-image-name
Installing and Using Cloudflare Tunnel in WHM
Perhaps you want to use Cloudflare Tunnel within your WHM/cPanel server. For web hosts, this could work well with the Cloudflare for SaaS product, allowing you to proxy custom hostnames from your customers via a single Zone in Cloudflare. If you only use WHM for your own hosting, you can skip Cloudflare for SaaS and simply put the Hostname into your DNS entries.
The installation commands in WHM are:
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
rpm -i cloudflared-linux-x86_64.rpm
cloudflared service install <Tunnel_Secret>
If you use ConfigServer Firewall, you need to allow outbound UDP 7844 access in for IPv4 and IPv6.
To check the status of the service:
sudo service cloudflared status -l
Step 7: Use the Cloudflare Tunnel Hostname in your DNS
Now that our Tunnel is up and running, you can make it available to the world. To do this, we update our DNS in Cloudflare to use a CNAME to point to our tunnel. The Tunnel Hostname is always in the format:
<Tunnel_ID>.cfargotunnel.com
This Tunnel Hostname is not accessible to the public internet – it will only work within a Proxied DNS Record in your Cloudflare Account.
Once you have saved your record, you can browse to the domain name and test it works!
Workaround: WordPress, SSL, and Cloudflare Tunnel
If you use Cloudflare Tunnel to send traffic to your web server on Port 80 (or any non-HTTPS Port), WordPress may get stuck in a redirect loop. This is a common challenge.
WordPress needs to know you’re accessing it securely. If you access Cloudflare via SSL, but then the Tunnel endpoint is non-SSL (e.g. localhost:80), then WordPress doesn’t know the communication has been via SSL up until that point.
You have two main options:
1. Trick WordPress into thinking it’s being accessed via SSL.
In wp-config.php, you should be able to add $_SERVER['HTTPS'] = 'on';
right near the top.
2. Setup a local SSL certificate on your host.
If you setup a self-signed SSL certificate or a Cloudflare Edge Certificate on your web server, you can then configure your tunnel to connect via HTTPS (tunnel route becomes https://localhost:443). However, you also need to configure your Tunnel to ignore SSL errors (self signed certificates will be rejected by default).
If you use Tunnel Remote Configuration, there’s no UI I’m aware to do this, but you can use the Clouflare API to do this:
curl -k -X PUT -H 'Content-Type: application/json' -H 'Authorization: Bearer <Cloudflare API Token>' -d '{
"config": {
"originRequest": {
"noTLSVerify": true
},
"warp-routing": {
"enabled": false
},
"ingress": [
{
"service": "https://127.0.0.1:443"
}
]
}
}' 'https://api.cloudflare.com/client/v4/accounts/<account_id>/cfd_tunnel/<tunnel_id>/configurations'
Of course, always test this on a test/development site first before modifying your live site.