Ports
Expose services running in your sandbox via public preview URLs. See Preview URLs concept for details.
Expose a port and get a preview URL for accessing services running in the sandbox.
const response = await sandbox.exposePort(port: number, options: ExposePortOptions): Promise<ExposePortResponse>Parameters:
port- Port number to expose (1024-65535)options:hostname- Your Worker's domain name (e.g.,'example.com'). Required to construct preview URLs with wildcard subdomains likehttps://8080-sandbox-abc123token.example.com. Cannot be a.workers.devdomain as it doesn't support wildcard DNS patterns.name- Friendly name for the port (optional)token- Custom token for the preview URL (optional). Must be 1-16 characters containing only lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_). If not provided, a random 16-character token is generated automatically.
Returns: Promise<ExposePortResponse> with port, url (preview URL), name
// Extract hostname from requestconst { hostname } = new URL(request.url);
// Basic usage with auto-generated tokenawait sandbox.startProcess("python -m http.server 8000");const exposed = await sandbox.exposePort(8000, { hostname });
console.log("Available at:", exposed.url);// https://8000-sandbox-id-abc123random.yourdomain.com
// With custom token for stable URLs across restartsconst stable = await sandbox.exposePort(8080, { hostname, token: "my_service_v1", // 1-16 chars: a-z, 0-9, _});console.log("Stable URL:", stable.url);// https://8080-sandbox-id-my_service_v1.yourdomain.com
// With custom token for stable URLs across deploymentsawait sandbox.startProcess("node api.js");const api = await sandbox.exposePort(3000, { hostname, name: "api", token: "prod-api-v1", // URL stays same across restarts});
console.log("Stable API URL:", api.url);// https://3000-sandbox-id-prod-api-v1.yourdomain.com
// Multiple services with custom tokensawait sandbox.startProcess("npm run dev");const frontend = await sandbox.exposePort(5173, { hostname, name: "frontend", token: "dev-ui",});// Extract hostname from requestconst { hostname } = new URL(request.url);
// Basic usage with auto-generated tokenawait sandbox.startProcess('python -m http.server 8000');const exposed = await sandbox.exposePort(8000, { hostname });
console.log('Available at:', exposed.url);// https://8000-sandbox-id-abc123random.yourdomain.com
// With custom token for stable URLs across restartsconst stable = await sandbox.exposePort(8080, { hostname, token: 'my_service_v1' // 1-16 chars: a-z, 0-9, _});console.log('Stable URL:', stable.url);// https://8080-sandbox-id-my_service_v1.yourdomain.com
// With custom token for stable URLs across deploymentsawait sandbox.startProcess('node api.js');const api = await sandbox.exposePort(3000, { hostname, name: 'api', token: 'prod-api-v1' // URL stays same across restarts});
console.log('Stable API URL:', api.url);// https://3000-sandbox-id-prod-api-v1.yourdomain.com
// Multiple services with custom tokensawait sandbox.startProcess('npm run dev');const frontend = await sandbox.exposePort(5173, { hostname, name: 'frontend', token: 'dev-ui'});Custom tokens enable consistent preview URLs across container restarts and deployments. This is useful for:
- Production environments - Share stable URLs with users or teams
- Development workflows - Maintain bookmarks and integrations
- CI/CD pipelines - Reference consistent URLs in tests or deployment scripts
Token Requirements:
- 1-16 characters in length
- Only lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_)
- Must be unique per sandbox (cannot reuse tokens across different ports)
// Production API with stable URLconst { url } = await sandbox.exposePort(8080, { hostname: "api.example.com", token: "v1-stable", // Always the same URL});
// Error: Token collision preventionawait sandbox.exposePort(8081, { hostname, token: "v1-stable" });// Throws: Token 'v1-stable' is already in use by port 8080
// Success: Re-exposing same port with same token (idempotent)await sandbox.exposePort(8080, { hostname, token: "v1-stable" });// Works - same port, same token// Production API with stable URLconst { url } = await sandbox.exposePort(8080, { hostname: 'api.example.com', token: 'v1-stable' // Always the same URL});
// Error: Token collision preventionawait sandbox.exposePort(8081, { hostname, token: 'v1-stable' });// Throws: Token 'v1-stable' is already in use by port 8080
// Success: Re-exposing same port with same token (idempotent)await sandbox.exposePort(8080, { hostname, token: 'v1-stable' });// Works - same port, same tokenValidate if a token is authorized to access a specific exposed port. Useful for custom authentication or routing logic.
const isValid = await sandbox.validatePortToken(port: number, token: string): Promise<boolean>Parameters:
port- Port number to checktoken- Token to validate
Returns: Promise<boolean> - true if token is valid for the port, false otherwise
// Custom validation in your Workerexport default { async fetch(request, env) { const url = new URL(request.url);
// Extract token from custom header or query param const customToken = request.headers.get("x-access-token");
if (customToken) { const sandbox = getSandbox(env.Sandbox, "my-sandbox"); const isValid = await sandbox.validatePortToken(8080, customToken);
if (!isValid) { return new Response("Invalid token", { status: 403 }); } }
// Handle preview URL routing const proxyResponse = await proxyToSandbox(request, env); if (proxyResponse) return proxyResponse;
// Your application routes return new Response("Not found", { status: 404 }); },};// Custom validation in your Workerexport default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url);
// Extract token from custom header or query param const customToken = request.headers.get('x-access-token');
if (customToken) { const sandbox = getSandbox(env.Sandbox, 'my-sandbox'); const isValid = await sandbox.validatePortToken(8080, customToken);
if (!isValid) { return new Response('Invalid token', { status: 403 }); } }
// Handle preview URL routing const proxyResponse = await proxyToSandbox(request, env); if (proxyResponse) return proxyResponse;
// Your application routes return new Response('Not found', { status: 404 }); }};Remove an exposed port and close its preview URL.
await sandbox.unexposePort(port: number): Promise<void>Parameters:
port- Port number to unexpose
await sandbox.unexposePort(8000);await sandbox.unexposePort(8000);Get information about all currently exposed ports.
const response = await sandbox.getExposedPorts(): Promise<GetExposedPortsResponse>Returns: Promise<GetExposedPortsResponse> with ports array (containing port, exposedAt, name)
const { ports } = await sandbox.getExposedPorts();
for (const port of ports) { console.log(`${port.name || port.port}: ${port.exposedAt}`);}const { ports } = await sandbox.getExposedPorts();
for (const port of ports) { console.log(`${port.name || port.port}: ${port.exposedAt}`);}Connect to WebSocket servers running in the sandbox. Use this when your Worker needs to establish WebSocket connections with services in the sandbox.
Common use cases:
- Route incoming WebSocket upgrade requests with custom authentication or authorization
- Connect from your Worker to get real-time data from sandbox services
For exposing WebSocket services via public preview URLs, use exposePort() with proxyToSandbox() instead. See WebSocket Connections guide for examples.
const response = await sandbox.wsConnect(request: Request, port: number): Promise<Response>Parameters:
request- Incoming WebSocket upgrade requestport- Port number (1024-65535, excluding 3000)
Returns: Promise<Response> - WebSocket response establishing the connection
import { getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";
export default { async fetch(request, env) { if (request.headers.get("Upgrade")?.toLowerCase() === "websocket") { const sandbox = getSandbox(env.Sandbox, "my-sandbox"); return await sandbox.wsConnect(request, 8080); }
return new Response("WebSocket endpoint", { status: 200 }); },};import { getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";
export default { async fetch(request: Request, env: Env): Promise<Response> { if (request.headers.get('Upgrade')?.toLowerCase() === 'websocket') { const sandbox = getSandbox(env.Sandbox, 'my-sandbox'); return await sandbox.wsConnect(request, 8080); }
return new Response('WebSocket endpoint', { status: 200 }); }};- Preview URLs concept - How preview URLs work
- Commands API - Start background processes