A simple Python library to expose local servers using Cloudflare Tunnel
Project description
suxitunnel
A simple Python library to expose your local server to the internet using Cloudflare's infrastructure.
Inspired by localtunnel-py but using Cloudflare's global network for better performance and DDoS protection.
Installation
pip install suxitunnel
Quick Start
Command Line
# Expose local port 8000 (random URL)
suxitunnel --port 8000
# Or use short form
suxitunnel -p 8000
# Use a named tunnel with dedicated URL (requires Cloudflare account)
suxitunnel --port 8000 --tunnel-token YOUR_TOKEN
Python API
from suxitunnel import Tunnel
# Expose local port 8000
tunnel = Tunnel(8000)
print(f"Your URL: {tunnel.url}")
# Keep your app running...
input("Press Enter to close tunnel...")
tunnel.close()
That's it! No configuration, no authentication, no manual setup.
Features
- 🚀 Zero Configuration - Just pass a port number
- 🔒 Secure - Uses Cloudflare's infrastructure with DDoS protection
- 🌍 Global CDN - Fast access from anywhere in the world
- 📦 Auto-install - Automatically downloads cloudflared if needed
- 🐍 Simple API - Clean, Pythonic interface
- 💻 CLI Support - Use from command line or as a library
- 🏷️ Named Tunnels - Support for dedicated URLs with tunnel tokens
Usage
Basic Usage
from suxitunnel import Tunnel
tunnel = Tunnel(8000)
print(tunnel.url) # https://random-name.trycloudflare.com
# Your local server is now public!
# Keep tunnel alive...
tunnel.close() # Close when done
Context Manager (Recommended)
from suxitunnel import Tunnel
with Tunnel(8000) as tunnel:
print(f"Public URL: {tunnel.url}")
# Do your work...
# Tunnel automatically closes when exiting the block
With Flask
from flask import Flask
from suxitunnel import Tunnel
import threading
app = Flask(__name__)
@app.route('/')
def index():
return "Hello from Flask!"
if __name__ == '__main__':
# Start tunnel in background
tunnel = Tunnel(5000)
print(f"🌐 Public URL: {tunnel.url}")
# Start Flask
app.run(port=5000)
With FastAPI
from fastapi import FastAPI
from suxitunnel import Tunnel
import uvicorn
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello from FastAPI!"}
if __name__ == "__main__":
# Start tunnel
tunnel = Tunnel(8000)
print(f"🌐 Public URL: {tunnel.url}")
# Start FastAPI
uvicorn.run(app, host="0.0.0.0", port=8000)
Check Tunnel Status
tunnel = Tunnel(8000)
print(tunnel.url) # Public URL
print(tunnel.is_alive()) # True if tunnel is running
print(tunnel.port) # 8000
print(tunnel) # <Tunnel port=8000 url=https://... status=active>
Custom Host
# Tunnel a service running on a different host
tunnel = Tunnel(8000, host="192.168.1.100")
Manual cloudflared Path
# Use specific cloudflared binary
tunnel = Tunnel(8000, cloudflared_path="/usr/local/bin/cloudflared")
Disable Auto-download
# Don't automatically download cloudflared
tunnel = Tunnel(8000, auto_download=False)
API Reference
Command Line Interface
suxitunnel [-h] -p PORT [--tunnel-token TUNNEL_TOKEN] [--local-https]
Arguments:
-p, --port PORT: Local port to expose (required)--tunnel-token TOKEN: Cloudflare tunnel token for named tunnels (optional)--local-https: Connect to local server via HTTPS (for self-signed certs)-h, --help: Show help message
Examples:
# Quick tunnel with random URL
suxitunnel --port 8000
# Named tunnel with dedicated URL
suxitunnel --port 8000 --tunnel-token eyJhIjoiNz...
# Local HTTPS server (e.g., .NET Core with HTTPS)
suxitunnel --port 5001 --local-https
Tunnel(port, host="localhost", cloudflared_path=None, auto_download=True, tunnel_token=None, local_https=False)
Create a tunnel to expose a local port.
Parameters:
port(int): Local port to expose (required)host(str): Local hostname (default: "localhost")cloudflared_path(str): Path to cloudflared binary (auto-detected if None)auto_download(bool): Auto-download cloudflared if not found (default: True)tunnel_token(str): Cloudflare tunnel token for named tunnels (optional)local_https(bool): Set to True if local server uses HTTPS (default: False)
Attributes:
url(str): Public HTTPS URL for the tunnelport(int): Local port being exposedhost(str): Local hostnametunnel_token(str): Tunnel token if using named tunnelis_alive()(bool): Check if tunnel is runningclose(): Close the tunnel
Example:
# Quick tunnel (random URL)
tunnel = Tunnel(8000)
print(tunnel.url) # https://abc123.trycloudflare.com
tunnel.close()
# Named tunnel (dedicated URL)
tunnel = Tunnel(8000, tunnel_token="your-token")
print(tunnel.url) # https://your-configured-domain.com
tunnel.close()
open_tunnel(port, host="localhost")
Convenience function to create a tunnel.
from suxitunnel import open_tunnel
tunnel = open_tunnel(8000)
How It Works
Technical Overview
This library uses Cloudflare Quick Tunnels (also called TryCloudflare), which:
- Creates an outbound HTTP/2 connection from your machine to Cloudflare's edge
- No inbound ports are opened on your machine (firewall-friendly!)
- No authentication or account required
- Traffic is routed through Cloudflare's global network
- Automatic HTTPS with valid certificate
Architecture:
Your App (localhost:8000) → cloudflared → Cloudflare Edge → Internet
(HTTP/2) (Global CDN) (HTTPS)
Cloudflared Binary
The library automatically downloads the cloudflared binary on first use if it's not already installed. The binary is saved to the library's installation directory.
Supported platforms:
- Linux (x64, ARM64)
- macOS (x64, ARM64/M1)
- Windows (x64)
Comparison with Alternatives
| Feature | suxitunnel | localtunnel | ngrok |
|---|---|---|---|
| Free tier | ✅ Unlimited | ✅ Yes | ⚠️ Limited |
| Custom domains | ❌ Random | ❌ Random | 💰 Paid |
| DDoS protection | ✅ Included | ❌ No | ⚠️ Limited |
| Global CDN | ✅ Yes | ❌ No | ⚠️ Limited |
| Auth required | ✅ No | ✅ No | ⚠️ Yes |
| Connection limits | ✅ None | ⚠️ Yes | ⚠️ Yes |
| Rate limits | ✅ Generous | ⚠️ Yes | ⚠️ Yes |
Limitations
Quick Tunnels (default):
- Random URLs: Each tunnel gets a random
*.trycloudflare.comURL - Temporary: Tunnels are meant for development/testing, not production
- No persistence: URL changes each time you create a new tunnel
- Rate limits: Subject to Cloudflare's fair use policy
Named Tunnels (with --tunnel-token):
- Requires a free Cloudflare account
- Provides persistent, dedicated URLs
- Better suited for staging/production environments
For setting up named tunnels, see the Cloudflare Tunnel documentation.
Security Considerations
What This Tunnel Provides
✅ DDoS protection - Cloudflare's network absorbs attacks
✅ HTTPS encryption - Valid SSL certificate included
✅ No exposed ports - Outbound connection only
✅ Firewall-friendly - Works behind NAT/firewalls
What You Still Need
⚠️ Application security - Implement authentication/authorization
⚠️ Input validation - Protect against malicious input
⚠️ Rate limiting - Prevent abuse of your endpoints
⚠️ Access control - Not everyone should access your tunnel
Important: Once your local server is exposed via a tunnel, anyone with the URL can access it. Make sure your application has proper authentication!
Best Practices
# ❌ DON'T: Expose services without authentication
tunnel = Tunnel(8000) # If port 8000 has no auth, anyone can access it!
# ✅ DO: Use authentication in your application
from flask import Flask, request, abort
from functools import wraps
def require_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.headers.get('Authorization')
if auth != 'Bearer YOUR_SECRET_TOKEN':
abort(401)
return f(*args, **kwargs)
return decorated
@app.route('/api/data')
@require_auth # Protected!
def get_data():
return {"data": "sensitive"}
Troubleshooting
"cloudflared not found"
The library should auto-download cloudflared. If it fails:
# Manual install
# macOS
brew install cloudflare/cloudflare/cloudflared
# Linux
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64
chmod +x cloudflared-linux-amd64
sudo mv cloudflared-linux-amd64 /usr/local/bin/cloudflared
# Or disable auto-download and specify path
tunnel = Tunnel(8000, cloudflared_path="/path/to/cloudflared", auto_download=False)
"Tunnel failed to start"
Check that:
- Your local service is running:
curl http://localhost:8000 - The port is not already in use
- You have internet connectivity
- Firewall allows outbound HTTPS (port 443)
Tunnel URL not appearing
The library waits up to 30 seconds for the URL. If it times out:
- Check your internet connection
- Verify cloudflared is working:
cloudflared --version - Try running manually:
cloudflared tunnel --url http://localhost:8000
Random disconnections
Quick Tunnels are designed for temporary use. For stable connections:
- Restart the tunnel when disconnected
- For production, use authenticated Cloudflare Tunnels with named tunnels
- Implement automatic reconnection logic
Examples
See the examples/ directory for complete working examples:
basic_example.py- Simplest usageflask_example.py- Flask integrationfastapi_example.py- FastAPI integrationcontext_manager.py- Using with context managermultiple_tunnels.py- Running multiple tunnels
Development
# Clone the repo
git clone https://github.com/sadwx/suxitunnel.git
cd suxitunnel
# Install in development mode
pip install -e .
# Run tests
python -m pytest tests/
License
MIT License - see LICENSE file for details.
Related Projects
- cloudflared - Official Cloudflare Tunnel client
- localtunnel-py - Similar library using localtunnel
- pyngrok - Python wrapper for ngrok
Credits
This library is a Python wrapper around cloudflared, the official Cloudflare Tunnel client.
Inspired by the simplicity of localtunnel-py.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file suxitunnel-0.1.3.tar.gz.
File metadata
- Download URL: suxitunnel-0.1.3.tar.gz
- Upload date:
- Size: 11.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8ce0e517a18a767e0e5f19e45b458d121862c2109fac2d6dca92bf9cbb82af8
|
|
| MD5 |
d0fcdf42516f80d8c3fb074fdcc1a8ce
|
|
| BLAKE2b-256 |
c1f4bd50a3f25634214ea5efa695da3990838da22717bdfc00ea33d71550e8a1
|
Provenance
The following attestation bundles were made for suxitunnel-0.1.3.tar.gz:
Publisher:
publish.yml on sadwx/suxitunnel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
suxitunnel-0.1.3.tar.gz -
Subject digest:
c8ce0e517a18a767e0e5f19e45b458d121862c2109fac2d6dca92bf9cbb82af8 - Sigstore transparency entry: 911010870
- Sigstore integration time:
-
Permalink:
sadwx/suxitunnel@fd3f51886ab621e783bb11162ccf222640e23047 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/sadwx
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fd3f51886ab621e783bb11162ccf222640e23047 -
Trigger Event:
push
-
Statement type:
File details
Details for the file suxitunnel-0.1.3-py3-none-any.whl.
File metadata
- Download URL: suxitunnel-0.1.3-py3-none-any.whl
- Upload date:
- Size: 25.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6882cad064535ec34a8b0f110f40d15b19dc6a472e3486db5eb31e357a8efbac
|
|
| MD5 |
db13137d49bbc6fc04210e3f2158ed2b
|
|
| BLAKE2b-256 |
70cdecf4ad8a677a5b5b027d197cd180b29415343aecacd64d9a90c05574f307
|
Provenance
The following attestation bundles were made for suxitunnel-0.1.3-py3-none-any.whl:
Publisher:
publish.yml on sadwx/suxitunnel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
suxitunnel-0.1.3-py3-none-any.whl -
Subject digest:
6882cad064535ec34a8b0f110f40d15b19dc6a472e3486db5eb31e357a8efbac - Sigstore transparency entry: 911010898
- Sigstore integration time:
-
Permalink:
sadwx/suxitunnel@fd3f51886ab621e783bb11162ccf222640e23047 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/sadwx
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fd3f51886ab621e783bb11162ccf222640e23047 -
Trigger Event:
push
-
Statement type: