Use mekong-django to run manage.py runserver and expose it via a public tunnel — useful for testing webhooks (Stripe, GitHub, Slack), demonstrating to clients, or testing on mobile devices.
Quick Start
pip install mekong-tunnel django# Same as: python manage.py runserver
mekong-djangoPublic URL: https://bright-moon-e5f6a7b8.mekongtunnel.dev — printed immediately.
Open in Browser
mekong-django --domain # Opens tunnel URL
mekong-django --local # Opens http://localhost:8000Custom Port
mekong-django 8080
# Same as: python manage.py runserver 8080Full Example
# From your Django project root (where manage.py lives)
cd myproject/
mekong-django --domainAll runserver arguments pass through:
mekong-django 0.0.0.0:8000 --settings=myproject.settings.devALLOWED_HOSTS Configuration
Django's security check requires tunnel hostnames to be in ALLOWED_HOSTS. The easiest fix:
# settings.py
import os
ALLOWED_HOSTS = [
'localhost',
'127.0.0.1',
'*.mekongtunnel.dev', # Allow all tunnel subdomains
]
# Or use django-cors-headers for more controlFor dynamic tunnel subdomains, you can also use a middleware:
# myapp/middleware.py
class AllowMekongTunnelMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
host = request.META.get('HTTP_HOST', '')
if host.endswith('.mekongtunnel.dev'):
request.META['SERVER_NAME'] = host.split('.')[0]
return self.get_response(request)# settings.py
MIDDLEWARE = [
'myapp.middleware.AllowMekongTunnelMiddleware',
...
]CSRF for Webhooks
Django's CSRF middleware will reject POST requests from external sources. For webhook endpoints, exempt them:
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
import json
@csrf_exempt
def stripe_webhook(request):
if request.method != 'POST':
return JsonResponse({'error': 'POST required'}, status=405)
payload = json.loads(request.body)
print("Event:", payload.get("type"))
return JsonResponse({"status": "ok"})# urls.py
from django.urls import path
from . import views
urlpatterns = [
path('webhooks/stripe/', views.stripe_webhook, name='stripe_webhook'),
]Django REST Framework
Works perfectly via tunnel. Example:
# views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['GET'])
def hello(request):
host = request.META.get('HTTP_HOST')
return Response({
"message": "Hello from Django!",
"tunnel_host": host,
})mekong-django --domain
# API: https://your-tunnel.mekongtunnel.dev/api/hello/Programmatic API
from mekong_tunnel import start_tunnel
import subprocess, sys
tunnel = start_tunnel(port=8000)
print("Public URL:", tunnel.url)
subprocess.run([sys.executable, 'manage.py', 'runserver', '8000'])Static Files
Django's dev server serves static files automatically when DEBUG=True. These are proxied through the tunnel normally.
For media files (uploads), ensure MEDIA_URL is configured and DEBUG=True for dev.
Environment Variables
MEKONG_TOKEN=your_token DJANGO_SETTINGS_MODULE=myproject.settings mekong-django --domainNotes
- Django's
runserverauto-reloads on code changes; the tunnel stays connected during reloads DEBUG=Truemust be set for Django's dev server to work properly- For production-grade serving, use
mekong-gunicornwithgunicorn myproject.wsgi:application