Preview URLs
Preview URLs provide public HTTPS access to services running inside sandboxes. When you expose a port, you get a unique 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);// Production: https://8000-abc123.example.com// Local dev: http://localhost:8787/...Production: https://{port}-{sandbox-id}.yourdomain.com
- Port 8080:
https://8080-abc123.example.com - Port 3000:
https://3000-abc123.example.com
Local development: http://localhost:8787/...
Preview URLs remain stable while a port is exposed and can be shared during that time. However, if you unexpose and re-expose a port, a new random token is generated and the URL changes. For persistent URLs, keep ports exposed for the duration you need them accessible.
You must call proxyToSandbox() first in your Worker's fetch handler to route preview URL requests:
import { proxyToSandbox, getSandbox } from "@cloudflare/sandbox";
export default { async fetch(request, env) { // Handle preview URL routing first const proxyResponse = await proxyToSandbox(request, env); if (proxyResponse) return proxyResponse;
// Your application routes // ... },};Requests flow: Browser → Your Worker → Durable Object (sandbox) → Your Service.
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://3000-abc123.example.com// https://3001-abc123.example.com- HTTP/HTTPS requests
- 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)
- WebSocket connections
- Ports outside range 1024-65535
- Port 3000 (used internally by the SDK)
Built-in security:
- Token-based access - Each exposed port gets a unique token in the URL (for example,
https://8080-sandbox-abc123token.example.com) - HTTPS in production - All traffic is encrypted with automatic TLS
- Unpredictable URLs - Tokens are randomly generated and difficult to guess
Add application-level authentication:
For additional security, implement authentication within your application:
from flask import Flask, request, abort
app = Flask(__name__)
@app.route('/data')def get_data(): # Check for your own authentication token auth_token = request.headers.get('Authorization') if auth_token != 'Bearer your-secret-token': abort(401) return {'data': 'protected'}This adds a second layer of security on top of the URL token.
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));For custom domain issues, see Production Deployment troubleshooting.
- Production Deployment - Set up custom domains for production
- Expose Services - Practical patterns for exposing ports
- Ports API - Complete API reference
- Security Model - Security best practices
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