INTEGRITY Документация

Preview URLs

Быстрое развёртывание

Для быстрых preview-развёртываний рекомендуем использовать Cloudflare Tunnel ↗ чтобы генерировать preview-URL для ваших веб-сервисов. Они работают как при локальной разработке, так и на workers.dev и в продакшене.

await sandbox.startProcess("python -m http.server 8000");
const tunnel = await sandbox.tunnels.get(8000);
console.log(tunnel.url);
// https://acute-llama-dancing-roundly.trycloudflare.app

// Request will be routed directly to the webserver running on the sandbox.
const req = await fetch(`${tunnel.url}/api/users`); // => GET http://localhost:8000/api/users

Поддержка Cloudflare Tunnel сейчас имеет следующие ограничения:

См. справочник по API туннелей для полного набора API и функций.

Использование в продакшене, стабильные URL и собственные домены

Для использования в продакшене рекомендуем использовать exposePort() API и маршрутизация трафика через ваш Worker.

Preview URL обеспечивают публичный доступ по HTTPS к сервисам, запущенным внутри sandbox. При открытии порта вы получаете уникальный URL, который проксирует запросы к вашему сервису.

// Extract hostname from request
const { hostname } = new URL(request.url);

await sandbox.startProcess("python -m http.server 8000");
const exposed = await sandbox.exposePort(8000, { hostname });

console.log(exposed.url);
// Production: https://8000-sandbox-id-abc123random4567.yourdomain.com
// Local dev: http://8000-sandbox-id-abc123random4567.localhost:{port}/

Формат URL

Production: https://{port}-{sandbox-id}-{token}.yourdomain.com

Локальная разработка: http://{port}-{sandbox-id}-{token}.localhost:{dev-server-port}

Типы токенов

Автоматически созданные токены (по умолчанию)

Если пользовательский токен не указан, генерируется случайный 16-символьный токен:

const exposed = await sandbox.exposePort(8000, { hostname });
// https://8000-sandbox-id-abc123random4567.yourdomain.com

URL-адреса с автоматически сгенерированными токенами меняются при закрытии и повторном открытии порта.

Пользовательские токены для стабильных URL

Для продакшен-развёртываний или общих URL укажите пользовательский токен, чтобы сохранить постоянство при перезапусках контейнера:

const stable = await sandbox.exposePort(8000, {
	hostname,
	token: "api_v1",
});
// https://8000-sandbox-id-api_v1.yourdomain.com
// Same URL every time ✓

Требования к токену:

Варианты использования пользовательских токенов:

Учёт регистра в ID

Preview URL извлекают ID sandbox из имени хоста для маршрутизации запросов. Поскольку имена хостов не чувствительны к регистру (в соответствии с RFC 3986), они всегда приводятся к нижнему регистру: 8080-MyProject-123.yourdomain.com становится 8080-myproject-123.yourdomain.com.

Проблема: Если вы создаёте песочницу с "MyProject-123", он существует в виде Durable Object с этим точным ID. Но preview URL направляет запросы на "myproject-123" (приведено к нижнему регистру от имени хоста). Это разные объекты Durable Object, поэтому песочница недоступна через preview URL.

// Problem scenario
const sandbox = getSandbox(env.Sandbox, "MyProject-123");
// Durable Object ID: "MyProject-123"
await sandbox.exposePort(8080, { hostname });
// Preview URL: 8080-myproject-123-token123.yourdomain.com
// Routes to: "myproject-123" (different DO - doesn't exist!)

Решение: Используйте normalizeId: true для приведения идентификаторов к нижнему регистру при создании песочниц:

const sandbox = getSandbox(env.Sandbox, "MyProject-123", {
	normalizeId: true,
});
// Durable Object ID: "myproject-123" (lowercased)
// Preview URL: 8080-myproject-123-token123.yourdomain.com
// Routes to: "myproject-123" (same DO - works!)

Без normalizeId: true, exposePort() выбрасывает ошибку, если ID содержит символы верхнего регистра.

Рекомендуемая практика: Используйте идентификаторы в нижнем регистре с самого начала ('my-project-123'). См. Параметры песочницы - normalizeId для получения подробностей.

Маршрутизация запросов

Необходимо вызвать proxyToSandbox() первым в обработчике fetch вашего Worker, чтобы маршрутизировать запросы preview URL:

import { proxyToSandbox, getSandbox } from "@cloudflare/sandbox";

export { Sandbox } 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
		// ...
	},
};

Поток запросов: браузер → ваш Worker → Durable Object (песочница) → ваш сервис.

Несколько портов

Публикуйте несколько сервисов одновременно:

// Extract hostname from request
const { hostname } = new URL(request.url);

await sandbox.startProcess("node api.js"); // Port 3000
await sandbox.startProcess("node admin.js"); // Port 3001

const api = await sandbox.exposePort(3000, { hostname, name: "api" });
const admin = await sandbox.exposePort(3001, { hostname, name: "admin" });

// Each gets its own URL with unique tokens:
// https://3000-abc123-random16chars01.yourdomain.com
// https://3001-abc123-random16chars02.yourdomain.com

Что работает

Что не работает

Поддержка WebSocket

Preview URL поддерживают соединения WebSocket. Когда запрос на обновление до WebSocket достигает открытого порта, уровень маршрутизации автоматически выполняет процедуру установления соединения.

// Extract hostname from request
const { hostname } = new URL(request.url);

// Start a WebSocket server
await sandbox.startProcess("bun run ws-server.ts 8080");
const { url } = await sandbox.exposePort(8080, { hostname });

// Clients connect using WebSocket protocol
// Browser: new WebSocket('wss://8080-abc123-token123.yourdomain.com')

// Your Worker routes automatically
export default {
	async fetch(request, env) {
		const proxyResponse = await proxyToSandbox(request, env);
		if (proxyResponse) return proxyResponse;
	},
};

Для сценариев пользовательской маршрутизации, где Worker должен определять, к какой песочнице или порту подключаться на основе свойств запроса, см. wsConnect() в API портов.

Безопасность

Встроенная безопасность:

Добавьте аутентификацию на уровне приложения:

Для дополнительной защиты реализуйте аутентификацию в своём приложении:

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'}

Это добавляет второй уровень защиты поверх токена URL.

Устранение неполадок

URL недоступен

Проверьте, что служба запущена и прослушивает соединения:

// 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));

Ошибки в продакшене

При проблемах с пользовательским доменом см. устранение неполадок с пользовательским доменом для Preview URL.

Локальная разработка