Preview URLs
Preview URLs provide public access to services running inside sandboxes. When you expose a port, you get a unique HTTPS URL that proxies requests to your service.
await sandbox.startProcess('python -m http.server 8000');const exposed = await sandbox.exposePort(8000);
console.log(exposed.exposedAt);// https://abc123-8000.sandbox.workers.dev
Preview URLs follow this pattern:
https://{sandbox-id}-{port}.sandbox.workers.dev
Examples:
- Port 3000:
https://abc123-3000.sandbox.workers.dev
- Port 8080:
https://abc123-8080.sandbox.workers.dev
URL stability: URLs remain the same for a given sandbox ID and port. You can share, bookmark, or use them in webhooks.
User's Browser ↓ HTTPSYour Worker ↓Durable Object (sandbox) ↓ HTTPYour Service (on exposed port)
Important: You must handle preview URL routing in your Worker using proxyToSandbox()
:
import { proxyToSandbox, getSandbox } from "@cloudflare/sandbox";
export default { async fetch(request, env) { // Route preview URL requests to sandboxes const proxyResponse = await proxyToSandbox(request, env); if (proxyResponse) return proxyResponse;
// Your custom routes here // ... }};
Without this, preview URLs won't work.
Expose multiple services simultaneously:
await sandbox.startProcess('node api.js'); // Port 3000await sandbox.startProcess('node admin.js'); // Port 3001
const api = await sandbox.exposePort(3000, { name: 'api' });const admin = await sandbox.exposePort(3001, { name: 'admin' });
// Each gets its own URL:// https://abc123-3000.sandbox.workers.dev// https://abc123-3001.sandbox.workers.dev
- HTTP/HTTPS requests
- WebSocket (WSS) via HTTP upgrade
- Server-Sent Events
- All HTTP methods (GET, POST, PUT, DELETE, etc.)
- Request and response headers
- Raw TCP/UDP connections
- Custom protocols (must wrap in HTTP)
- Ports 80/443 (use 1024+)
Add authentication in your service:
from flask import Flask, request, abort
app = Flask(__name__)
@app.route('/data')def get_data(): token = request.headers.get('Authorization') if token != 'Bearer secret-token': abort(401) return {'data': 'protected'}
Security features:
- All traffic is HTTPS (automatic TLS)
- URLs use random sandbox IDs (hard to guess)
- You control authentication in your service
Check if service is running and listening:
// 1. Is service running?const processes = await sandbox.listProcesses();
// 2. Is port exposed?const ports = await sandbox.getExposedPorts();
// 3. Is service binding to 0.0.0.0 (not 127.0.0.1)?// Good:app.run(host='0.0.0.0', port=3000)
// Bad (localhost only):app.run(host='127.0.0.1', port=3000)
Service design:
- Bind to
0.0.0.0
to make accessible - Add authentication (don't rely on URL secrecy)
- Include health check endpoints
- Handle CORS if accessed from browsers
Cleanup:
- Unexpose ports when done:
await sandbox.unexposePort(port)
- Stop processes:
await sandbox.killAllProcesses()
- Ports API reference - Complete port exposure API
- Expose services guide - Practical patterns
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-