通过公共预览 URL 暴露在 sandbox 中运行的服务。详情请参阅 Preview URLs 概念。
将传入的 HTTP 和 WebSocket 请求路由到正确的 sandbox 容器。在 Worker 的 fetch 处理程序顶部、任何应用逻辑之前调用此函数,以便它自动拦截并转发预览 URL 请求。
proxyToSandbox(request: Request, env: Env): Promise<Response | null>参数:
request- 来自fetch处理程序的传入Request对象。env- 包含 Sandbox 绑定的Env对象。
返回值:Promise<Response | null> — 若请求匹配预览 URL 并已路由到 sandbox,则返回 Response;若不匹配且应由应用逻辑处理,则返回 null。
该函数检查请求主机名,以确定是否匹配已暴露端口的子域模式(例如 8080-sandbox-id-token.yourdomain.com)。若匹配,proxyToSandbox() 会将请求代理到正确的 Durable Object,并由 sandbox 服务处理。同时支持 HTTP 与 WebSocket 升级请求。
import { proxyToSandbox, getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";
export default {
async fetch(request, env) {
// Always call proxyToSandbox first to handle preview URL requests
const proxyResponse = await proxyToSandbox(request, env);
if (proxyResponse) return proxyResponse;
// Your application routes
const sandbox = getSandbox(env.Sandbox, "my-sandbox");
// ...
return new Response("Not found", { status: 404 });
},
};import { proxyToSandbox, getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Always call proxyToSandbox first to handle preview URL requests
const proxyResponse = await proxyToSandbox(request, env);
if (proxyResponse) return proxyResponse;
// Your application routes
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
// ...
return new Response('Not found', { status: 404 });
}
};暴露端口并获取用于访问 sandbox 中运行服务的预览 URL。
const response = await sandbox.exposePort(port: number, options: ExposePortOptions): Promise<ExposePortResponse>参数:
port- 要暴露的端口号(1024-65535)options:hostname- 你的 Worker 域名(例如'example.com')。构建带通配符子域的预览 URL(如https://8080-sandbox-abc123token.example.com)时必需。不能是.workers.dev域名,因为其不支持通配符 DNS 模式。name- 端口的友好名称(可选)token- 预览 URL 的自定义 token(可选)。必须为 1–16 个字符,仅含小写字母 (a-z)、数字 (0-9)、连字符 (-) 和下划线 (_)。若未提供,将自动生成 16 字符的随机 token。
返回值:Promise<ExposePortResponse>,包含 port、url(预览 URL)、name
// Extract hostname from request
const { hostname } = new URL(request.url);
// Basic usage with auto-generated token
await 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 restarts
const 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 deployments
await 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 tokens
await sandbox.startProcess("npm run dev");
const frontend = await sandbox.exposePort(5173, {
hostname,
name: "frontend",
token: "dev-ui",
});// Extract hostname from request
const { hostname } = new URL(request.url);
// Basic usage with auto-generated token
await 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 restarts
const 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 deployments
await 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 tokens
await sandbox.startProcess('npm run dev');
const frontend = await sandbox.exposePort(5173, {
hostname,
name: 'frontend',
token: 'dev-ui'
});自定义 token 可在容器重启和部署之间保持一致的预览 URL。这对以下场景很有用:
- 生产环境 - 与用户或团队分享稳定 URL
- 开发工作流 - 维护书签与集成
- CI/CD 流水线 - 在测试或部署脚本中引用一致的 URL
Token 要求:
- 长度为 1–16 个字符
- 仅限小写字母 (a-z)、数字 (0-9)、连字符 (-) 和下划线 (_)
- 每个 sandbox 内必须唯一(不能在不同端口间复用 token)
// Production API with stable URL
const { url } = await sandbox.exposePort(8080, {
hostname: "api.example.com",
token: "v1-stable", // Always the same URL
});
// Error: Token collision prevention
await 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 URL
const { url } = await sandbox.exposePort(8080, {
hostname: 'api.example.com',
token: 'v1-stable' // Always the same URL
});
// Error: Token collision prevention
await 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验证 token 是否有权访问特定已暴露端口。对自定义身份验证或路由逻辑很有用。
const isValid = await sandbox.validatePortToken(port: number, token: string): Promise<boolean>参数:
port- 要检查的端口号token- 要验证的 token
返回值:Promise<boolean> - 若 token 对该端口有效则为 true,否则为 false
// Custom validation in your Worker
export 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 Worker
export 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 });
}
};移除已暴露端口并关闭其预览 URL。
await sandbox.unexposePort(port: number): Promise<void>参数:
port- 要取消暴露的端口号
await sandbox.unexposePort(8000);await sandbox.unexposePort(8000);获取当前所有已暴露端口的信息。
const response = await sandbox.getExposedPorts(): Promise<GetExposedPortsResponse>返回值:Promise<GetExposedPortsResponse>,包含 ports 数组(含 port、url、name)
const { ports } = await sandbox.getExposedPorts();
for (const port of ports) {
console.log(`${port.name || port.port}: ${port.url}`);
}const { ports } = await sandbox.getExposedPorts();
for (const port of ports) {
console.log(`${port.name || port.port}: ${port.url}`);
}连接到在 sandbox 中运行的 WebSocket 服务器。当 Worker 需要与 sandbox 中的服务建立 WebSocket 连接时使用。
常见用例:
- 使用自定义身份验证或授权路由传入的 WebSocket 升级请求
- 从 Worker 连接以获取 sandbox 服务的实时数据
若要通过公共预览 URL 暴露 WebSocket 服务,请改用 exposePort() 与 proxyToSandbox()。示例请参阅 WebSocket 连接指南。
const response = await sandbox.wsConnect(request: Request, port: number): Promise<Response>参数:
request- 传入的 WebSocket 升级请求port- 端口号(1024-65535,不包括 3000)
返回值:Promise<Response> - 建立连接的 WebSocket 响应
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 概念 - 预览 URL 的工作原理
- 暴露服务指南 - 启动服务、暴露端口与路由请求的完整工作流
- WebSocket 连接指南 - 通过预览 URL 进行 WebSocket 路由
- Commands API - 启动后台进程
- Tunnels API - 用于快速开发的零配置
*.trycloudflare.comURL