← Sandbox SDK / sandbox / concepts
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 сейчас имеет следующие ограничения:
- Нет контроля над сгенерированным URL.
- Нет механизма аутентификации, кроме случайно сгенерированного URL.
- Каждый URL использует дополнительный
cloudflaredпроцесс в sandbox.
См. справочник по 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
- С автоматически сгенерированным токеном:
https://8080-abc123-random16chars12.yourdomain.com - С пользовательским токеном:
https://8080-abc123-my_api_v1.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.comURL-адреса с автоматически сгенерированными токенами меняются при закрытии и повторном открытии порта.
Пользовательские токены для стабильных URL
Для продакшен-развёртываний или общих URL укажите пользовательский токен, чтобы сохранить постоянство при перезапусках контейнера:
const stable = await sandbox.exposePort(8000, {
hostname,
token: "api_v1",
});
// https://8000-sandbox-id-api_v1.yourdomain.com
// Same URL every time ✓Требования к токену:
- длиной от 1 до 16 символов
- Только строчные буквы (a-z), цифры (0-9) и символы подчеркивания (_)
- Должно быть уникальным в пределах каждой песочницы
Варианты использования пользовательских токенов:
- Продакшен-API со стабильными эндпоинтами
- Предоставление URL-адресов демонстраций внешним пользователям
- Документация с единообразными примерами
- Интеграционное тестирование с предсказуемыми URL
Учёт регистра в 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Что работает
- HTTP/HTTPS-запросы
- WebSocket-соединения
- Server-Sent Events
- Все HTTP-методы (GET, POST, PUT, DELETE и другие)
- Заголовки запроса и ответа
Что не работает
- Необработанные TCP/UDP-соединения
- Пользовательские протоколы (должны быть обёрнуты в HTTP)
- Порты за пределами диапазона 1024-65535
- Порт 3000 (используется внутри SDK)
Поддержка 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 портов.
Безопасность
Встроенная безопасность:
- Доступ на основе токена - Каждый открытый порт получает уникальный токен в URL (например,
https://8080-sandbox-abc123token456.yourdomain.com) - HTTPS в рабочей среде - Весь трафик шифруется с помощью TLS. Сертификаты для wildcard-доменов первого уровня выпускаются автоматически (
*.yourdomain.com). Если ваш Worker работает на субдомене, см. Примечание о TLS для пользовательских доменов. - Непредсказуемые URL-адреса - Автоматически создаваемые токены генерируются случайным образом, и их сложно угадать
- Предотвращение коллизий токенов - Пользовательские токены проверяются на уникальность в пределах каждой песочницы
Добавьте аутентификацию на уровне приложения:
Для дополнительной защиты реализуйте аутентификацию в своём приложении:
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.
Локальная разработка
Связанные ресурсы
- Настройте preview URL на собственном домене - Подстановочный DNS и TLS для
exposePort() - Разверните приложение Sandbox - Развертывание Worker и образа контейнера
- Публикация сервисов - Практические шаблоны для открытия портов
- API портов - Полный справочник по API
- Tunnels API - Не требует настройки
*.trycloudflare.comURL как альтернатива для разработки - Модель безопасности - Рекомендации по безопасности