Setting up a public-facing server from your internal network usually comes with a major headache — you need a static IP and a DNS provider. Even if you’ve got your domain name ready, your ISP may not assign you a static IP, or worse, may charge extra for it.
But what if I told you there’s a way to bypass that entirely?
🔐 Enter Cloudflare Tunnel (aka cloudflared)
Cloudflare Tunnel allows you to expose your local server securely to the internet without needing a static IP or poking holes in your firewall. The magic? It establishes a persistent outbound connection from your machine to Cloudflare’s edge network.
So, instead of users directly hitting your home IP, their requests go through Cloudflare, which then forwards traffic through this tunnel to your internal server — and sends responses back the same way.
All you need is a registered domain on Cloudflare. No static IP, no port forwarding, and no exposing your machine directly.
🛠️ Setting Up a Cloudflare Tunnel
Let’s walk through the setup with a simple example.
1. Go to Cloudflare Zero Trust Dashboard
Navigate to the Zero Trust section in your Cloudflare dashboard.
2. Create Your Team Domain
Pick any name for your team domain. This is just for internal structuring.
3. Select the Free Plan
Yes, it’s completely free! Just proceed to payment, enter any method (no actual charges).
4. Navigate to “Networks > Tunnels”
On the sidebar, head to Networks → Tunnels , then click “Add a Tunnel.”
6. Start the Tunnel (Pick Your Environment)
You’ll now get setup instructions based on your OS.
- For macOS: brew install cloudflared and run as a service.
- Docker: You can also run it inside Docker — straightforward and clean.
Once done, your tunnel status should show as Connected.
🌐 Host Something Locally
Let’s test it out by running a simple server:
Option 1: NGINX via Docker
docker run -p 8080:80 nginx
Option 2: A Tiny Flask App
Save this as app.py and run with python app.py:
from flask import Flask
app = Flask( __name__ )
@app.route('/')
def home():
return 'Hello, Flask!'
if __name__ == ' __main__':
app.run(debug=True)
🌍 Expose It with a Public Hostname
Once the tunnel is active:
- Add a new public hostname
- Choose any subdomain (e.g., demo.yourdomain.com)
- Type: HTTP
- URL: localhost:8080 (for NGINX) or localhost:5000 (for Flask)
Save it, and boom — you now have a public link to your local server!
🐛 Facing issue with HTTPS request
In my setup:
- Next.js frontend ran on port 3000 and was exposed via a tunnel to a subdomain.
- Python Flask backend ran on port 5000, also exposed via a tunnel.
Both were running with HTTP internally.
Cloudflare provides an HTTPS URL for every tunnel. So even though my servers were running on HTTP, the frontend was making HTTPS requests to the backend.
This caused CORS or SSL handshake failures. The requests were made over HTTPS but seemed to break mid-way, possibly due to internal HTTP fallback.
Solution
I started the Flask server with an adhoc self-signed SSL certificate :
flask run --cert=adhoc --host=0.0.0.0
While setting up the public hostname in the tunnel:
- I used: https://:5000
- Changed the TLS setting to “No TLS verify” , allowing Cloudflare to accept self-signed certs.
Since Flask was generating the certificate on the fly, this was essential to avoid SSL errors.
🧠 Final Thoughts
Cloudflare Tunnel is a game-changer for self-hosting. Whether you’re building demos, internal tools, or working on projects that need remote access, you can now skip the static IP drama.
Try it out, and give your side projects a proper home on the internet — without compromising on security or simplicity.
If you face any issues while setting this up, feel free to reach out. Always happy to help!
—
Refs:
202504111902
Top comments (0)