blog.thms.uk

Using a CloudFlare Tunnel to share a local development site over the internet

The other day I needed to share a local development site with someone. One option to do this is ngrok, and indeed I have used it in the past. Another option is setting up a CloudFlare Tunnel, and if you already use CloudFlare for anything, that has some advantages.

This post assumes you already have cloudflared installed. If you are using macOS you can install it using homebrew, otherwise please see CloudFlare's download page:

brew install cloudflare/cloudflare/cloudflared

Setting up your first CloudFlare Tunnel

Let's firstly talk about the quickest way of sharing a site using a CloudFlare Tunnel. You can make use of CloudFlare's Quick Tunnels by simply running the cloudflared tunnel command. (Replace 8080 with whatever port your local server is running on):

cloudflared tunnel --url http://localhost:8080

cloudflared will generate a random subdomain of trycloudflare.com (for example https://wise-juvenile-pink-recovered.trycloudflare.com) and print it in the terminal for you to use and share. A TLS certificate is automatically generated, and all traffic is securely routed through CloudFlare's network. No need to open any ports in your firewall, or anything else, really. It just works.

Using a CloudFlare Tunnel with your own domain.

However, one of the really neat things about CloudFlare Tunnels is that I can use my own domain name for the shared test site. So, instead of sharing my site at https://wise-juvenile-pink-recovered.trycloudflare.com I could share it at https://dev.thms.uk, which is clearly much nicer. This only works if you are using CloudFlare' name servers though.

If you want to you use your own domain for the CloudFlare Tunnel, you need to start by authenticating cloudflared:

cloudflared tunnel login

This will open a browser window, where you log into CloudFlare, and authorise your local cloudflared to make the required changes to your account. It will save the required authentication info in the ~/.cloudflared/ directory.

Secondly, create a tunnel:

cloudflared tunnel create mytunnel

Here mytunnel is simply the name of your tunnel. As far as I can see it won't be visible to anyone but you, so choose whatever you want. This will produce an output like the below:

Tunnel credentials written to ~/.cloudflared/{uuid}.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.

Created tunnel mytunnel with id {uuid}

Now you need to create a configuration file for your tunnel. Create a file at ~/.cloudflared/config.yaml (the same directory in which the json file has been saved in the previous step), with the following content:

tunnel: {uuid}
credentials-file: ~/.cloudflared/{uuid}.json

ingress:
  - hostname: dev.thms.uk
    service: http://localhost:8080
  - service: http_status:404

Replace the {uuid} with the UUID printed by cloudflared in the previous step. Also replace dev.thms.uk with whatever domain you want to use publicly. If your local server isn't running on port 8080 you'll need to adjust that too.

Finally, create the DNS records needed, and run the tunnel:

cloudflared tunnel route dns mytunnel dev.thms.uk
cloudflared tunnel run mytunnel

That's it. Your tunnel should now be active, and you should be able to access your test site using the URL https://dev.thms.uk. Again: A TLS certificate has been generated automatically, and you won't need to open any ports (on the contrary: You'll want to actively close all ports in your firewall for security reasons!), as all traffic is securely tunnelled through CloudFlare's network.

In fact, this would be good enough for production access, if you wanted to host a public site on your home network, without having to open ports, configure a static IP address, etc. (Though you'll want to run cloudflared as a service for production use.)

Altering the Host header when proxying

I usually use Laravel Valet as my local web server. Valet detects which of my dev sites to serve based on the Host header of the incoming request.

E.g. Valet links http://site1.test to my ~/git/site1 directory, and http://site2.test to ~/git/site2.

Thankfully, I can instruct CloudFlare to rewrite the Host header for the incoming request, by adding the httpHostHeader key to the config.yaml file:

tunnel: {uuid}
credentials-file: /Users/{user}/.cloudflared/{uuid}.json

ingress:
  - hostname: site1.thms.uk
    service: http://localhost:8080
    originRequest:
       httpHostHeader: site1.test

Now Valet will serve my local site from http://site1.test through the CloudFlare Tunnel at https://site1.thms.uk.

How to add a further site/domain/host?

If you want to share a further site from your local computer, you do not need to run a second tunnel. Instead, you simply add it to the existing one, using the ingress rules in your config.yaml file. For example:

tunnel: {uuid}
credentials-file: /Users/{user}/.cloudflared/{uuid}.json

ingress:
  - hostname: site1.thms.uk
    service: http://localhost:8080
    originRequest:
       httpHostHeader: site1.test
  - hostname: site2.thms.uk
    service: http://localhost:8080
    originRequest:
       httpHostHeader: site2.test

Then add the DNS record and run (or restart) your tunnel:

cloudflared tunnel route dns mytunnel site2.thms.uk
cloudflared tunnel run mytunnel

Summary

Whilst you can set up a Quick Tunnel really easily with CloudFlare, you are missing out on the best parts when doing so. The original setup of a proper CloudFlare Tunnel is a bit more involved than ngrok, but once done, it's easy to add sites/domains to tunnel, and that's pretty cool. I do like being able to share test sites on my own domain, too.

I did feel that performance was significantly faster than I remember ngrok to be too.

So, I think I'll be using this in preference over ngrok in the future.