Expose your local development environment with Cloudflare Tunnels

Need to expose your local development environment to someone outside of your network? Cloudflare Tunnels is a free and fast alternative to popular tools like ngrok and localtunnel. You can easily access your local machine from anywhere. Plus, it outperforms the competition in terms of speed.

Expose your local development environment with Cloudflare Tunnels
Image from Tom Dahm

Have you ever needed to show your local development environment to someone who is not in the same network as you? Most likely to show something to a Product Owner or to test if a social media integration worked correctly.

Because of NAT and firewalls, you can't just send your machine's IP to someone else to access it. This is where tunneling tools come into play. The most known ones are ngrok and localtunnel.

They work but their free tiers slow or block parallel requests. A capability that has become a must have with modern bundlers like Vite as they create a plethora of parallel requests.

What's the alternative?

Cloudflare tunnels can also expose your local dev setup. It's free, faster and works without having to register.

To get it running, you'll need to install their cli tool.

Install cloudflared

On MacOS, the easiest way to cloudflared is with brew.

brew install cloudflared

For windows and linux based operating systems, there are binaries available here.

Forwarding a local port

To forward a specific port running on your machine, execute the following line

cloudflared --url http://localhost:8000

replace 8000 with the port you want to forward. cloudflared will then output the URL you can use to access your local machine. In our case it's https://recreation-camps-usr-continental.trycloudflare.com

The full output of the command is

cloudflared --url http://localhost:8000
Thank you for trying Cloudflare Tunnel. Doing so, without a Cloudflare account, is a quick way to experiment and try it out. However, be aware that these account-less Tunnels have no uptime guarantee. If you intend to use Tunnels in production you should use a pre-created named tunnel by following: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps
Requesting new quick Tunnel on trycloudflare.com...
+--------------------------------------------------------------------------------------------+
|  Your quick Tunnel has been created! Visit it at (it may take some time to be reachable):  |
|  https://recreation-camps-usr-continental.trycloudflare.com                                |
+--------------------------------------------------------------------------------------------+
Cannot determine default configuration path. No file [config.yml config.yaml] in [~/.cloudflared ~/.cloudflare-warp ~/cloudflare-warp /etc/cloudflared /usr/local/etc/cloudflared]
Version 2023.5.0
GOOS: darwin, GOVersion: go1.19.8, GoArch: arm64
Settings: map[ha-connections:1 protocol:quic url:http://localhost:8000]
Generated Connector ID: 48eb60ba-646f-4847-9173-c4ed98d5e326
cloudflared will not automatically update if installed by a package manager.
Initial protocol quic
ICMP proxy will use 192.168.20.116 as source for IPv4
ICMP proxy will use fe80::109a:88d9:c4f:7dda in zone en0 as source for IPv6
Created ICMP proxy listening on 192.168.20.116:0
Created ICMP proxy listening on [fe80::109a:88d9:c4f:7dda%en0]:0
Starting metrics server on 127.0.0.1:54026/metrics
Registered tunnel connection connIndex=0 connection=8e16208a-4334-451a-bfba-19e82e90c934 event=0 ip=198.41.200.193 location=SIN protocol=quic

How much better is it?

To compare the performance, I set up a test scenario serving a 30kb text file through each tunnel and requested it 100 times with a concurrency of 5 using Apache Bench:

ab -n 100 -c 5 [URL]

The results confirm my suspicion of cloudflared being faster than the competition:

Service time to completion failed requests median time longest request
cloudflared 8.895s 0 445ms 762ms
localtunnel 48.171s 0 2408ms 5331ms
ngrok 10.181s 70 509ms 4411ms

While ngrok is also quite fast, it starts blocking requests quickly in their free tier whereas localtunneljust starts to slow you down. With cloudflared no slowdown occurs and no requests are blocked. The concrete test results can be found at the bottom of this article.

Conclusion

  • faster responses
  • easy to set up
  • no time limitation (as with ngrok)

Using cloudflared is faster and it's easy to set up. Try out Cloudflare Tunnels today and experience the benefits for yourself!

If you found this article helpful, please share it with your colleagues and friends!

Bonus: Shorthand

If cloudflared --url is too long for you, here's a way to make it even shorter:

Mac OS

echo "alias tunnel='cloudflared --url'" >> ~/.zshrc

Linux / WSL

echo "alias tunnel='cloudflared --url'" >> ~/.bashrc

To forward a port, just type

tunnel http://localhost:8000

Testdata

Cloudflared

Concurrency Level:      5
Time taken for tests:   8.895 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      3047200 bytes
HTML transferred:       3022600 bytes
Requests per second:    11.24 [#/sec] (mean)
Time per request:       444.775 [ms] (mean)
Time per request:       88.955 [ms] (mean, across all concurrent requests)
Transfer rate:          334.53 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       79  159  65.4    151     607
Processing:   107  253 116.7    205     589
Waiting:       98  183  87.0    147     520
Total:        237  412 139.8    380     762

Percentage of the requests served within a certain time (ms)
  50%    380
  66%    458
  75%    514
  80%    544
  90%    635
  95%    713
  98%    747
  99%    762
 100%    762 (longest request)

Localtunnel

Concurrency Level:      5
Time taken for tests:   48.171 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      3055200 bytes
HTML transferred:       3022600 bytes
Requests per second:    2.08 [#/sec] (mean)
Time per request:       2408.552 [ms] (mean)
Time per request:       481.710 [ms] (mean, across all concurrent requests)
Transfer rate:          61.94 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      834 1026 254.9    916    1897
Processing:   869 1258 538.1   1176    4422
Waiting:      566  671 187.9    608    1726
Total:       1752 2284 611.3   2097    5331

Percentage of the requests served within a certain time (ms)
  50%   2097
  66%   2272
  75%   2518
  80%   2622
  90%   3040
  95%   3757
  98%   4240
  99%   5331
 100%   5331 (longest request)

ngrok

Concurrency Level:      5
Time taken for tests:   10.181 seconds
Complete requests:      100
Failed requests:        70
   (Connect: 0, Receive: 0, Length: 70, Exceptions: 0)
Non-2xx responses:      70
Total transferred:      1013080 bytes
HTML transferred:       987420 bytes
Requests per second:    9.82 [#/sec] (mean)
Time per request:       509.031 [ms] (mean)
Time per request:       101.806 [ms] (mean, across all concurrent requests)
Transfer rate:          97.18 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      149  240 428.0    179    3249
Processing:    44  241 718.6     66    4236
Waiting:       42  142 426.7     60    4168
Total:        201  482 880.0    252    4411

Percentage of the requests served within a certain time (ms)
  50%    252
  66%    272
  75%    335
  80%    356
  90%    471
  95%   3861
  98%   4402
  99%   4411
 100%   4411 (longest request)