Skip to content

Commit 9d62ab8

Browse files
committed
Document preview URL setup
1 parent be43cff commit 9d62ab8

File tree

5 files changed

+148
-54
lines changed

5 files changed

+148
-54
lines changed

src/content/docs/sandbox/api/ports.mdx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ sidebar:
77

88
import { TypeScriptExample } from "~/components";
99

10+
:::note[Production requires custom domain]
11+
Preview URLs require a custom domain with wildcard DNS routing in production. See [Production Deployment](/sandbox/guides/production-deployment/).
12+
:::
13+
1014
Expose services running in your sandbox via public preview URLs. See [Preview URLs concept](/sandbox/concepts/preview-urls/) for details.
1115

1216
## Methods
@@ -32,7 +36,7 @@ await sandbox.startProcess('python -m http.server 8000');
3236
const exposed = await sandbox.exposePort(8000);
3337
3438
console.log('Available at:', exposed.exposedAt);
35-
// https://abc123-8000.sandbox.workers.dev
39+
// https://8000-abc123.example.com
3640
3741
// Multiple services with names
3842
await sandbox.startProcess('node api.js');

src/content/docs/sandbox/concepts/preview-urls.mdx

Lines changed: 33 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,54 @@ sidebar:
55
order: 5
66
---
77

8-
Preview URLs provide public access to services running inside sandboxes. When you expose a port, you get a unique HTTPS URL that proxies requests to your service.
8+
:::note[Production requires custom domain]
9+
Preview URLs work in local development without configuration. For production, you need a custom domain with wildcard DNS routing. See [Production Deployment](/sandbox/guides/production-deployment/).
10+
:::
11+
12+
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.
913

1014
```typescript
1115
await sandbox.startProcess('python -m http.server 8000');
1216
const exposed = await sandbox.exposePort(8000);
1317

1418
console.log(exposed.exposedAt);
15-
// https://abc123-8000.sandbox.workers.dev
19+
// Production: https://8000-abc123.example.com
20+
// Local dev: http://localhost:8787/...
1621
```
1722

18-
## URL format
23+
## URL Format
1924

20-
Preview URLs follow this pattern:
21-
22-
```
23-
https://{sandbox-id}-{port}.sandbox.workers.dev
24-
```
25+
**Production**: `https://{port}-{sandbox-id}.yourdomain.com`
2526

26-
**Examples**:
27-
- Port 3000: `https://abc123-3000.sandbox.workers.dev`
28-
- Port 8080: `https://abc123-8080.sandbox.workers.dev`
27+
- Port 8080: `https://8080-abc123.example.com`
28+
- Port 3000: `https://3000-abc123.example.com`
2929

30-
**URL stability**: URLs remain the same for a given sandbox ID and port. You can share, bookmark, or use them in webhooks.
30+
**Local development**: `http://localhost:8787/...`
3131

32-
## Request routing
32+
URLs remain stable for a given sandbox ID and port. You can share, bookmark, or use them in webhooks.
3333

34-
```
35-
User's Browser
36-
↓ HTTPS
37-
Your Worker
38-
39-
Durable Object (sandbox)
40-
↓ HTTP
41-
Your Service (on exposed port)
42-
```
34+
## Request Routing
4335

44-
**Important**: You must handle preview URL routing in your Worker using `proxyToSandbox()`:
36+
You must call `proxyToSandbox()` first in your Worker's fetch handler to route preview URL requests:
4537

4638
```typescript
4739
import { proxyToSandbox, getSandbox } from "@cloudflare/sandbox";
4840

4941
export default {
5042
async fetch(request, env) {
51-
// Route preview URL requests to sandboxes
43+
// Handle preview URL routing first
5244
const proxyResponse = await proxyToSandbox(request, env);
5345
if (proxyResponse) return proxyResponse;
5446

55-
// Your custom routes here
47+
// Your application routes
5648
// ...
5749
}
5850
};
5951
```
6052

61-
Without this, preview URLs won't work.
53+
Requests flow: Browser → Your Worker → Durable Object (sandbox) → Your Service.
6254

63-
## Multiple ports
55+
## Multiple Ports
6456

6557
Expose multiple services simultaneously:
6658

@@ -72,19 +64,19 @@ const api = await sandbox.exposePort(3000, { name: 'api' });
7264
const admin = await sandbox.exposePort(3001, { name: 'admin' });
7365

7466
// Each gets its own URL:
75-
// https://abc123-3000.sandbox.workers.dev
76-
// https://abc123-3001.sandbox.workers.dev
67+
// https://3000-abc123.example.com
68+
// https://3001-abc123.example.com
7769
```
7870

79-
## What works
71+
## What Works
8072

8173
- HTTP/HTTPS requests
8274
- WebSocket (WSS) via HTTP upgrade
8375
- Server-Sent Events
8476
- All HTTP methods (GET, POST, PUT, DELETE, etc.)
8577
- Request and response headers
8678

87-
## What doesn't work
79+
## What Does Not Work
8880

8981
- Raw TCP/UDP connections
9082
- Custom protocols (must wrap in HTTP)
@@ -96,7 +88,7 @@ const admin = await sandbox.exposePort(3001, { name: 'admin' });
9688
Preview URLs are publicly accessible. Anyone with the URL can access your service.
9789
:::
9890

99-
**Add authentication in your service**:
91+
Add authentication in your service:
10092

10193
```python
10294
from flask import Flask, request, abort
@@ -118,7 +110,7 @@ def get_data():
118110

119111
## Troubleshooting
120112

121-
### URL not accessible
113+
### URL Not Accessible
122114

123115
Check if service is running and listening:
124116

@@ -137,19 +129,11 @@ app.run(host='0.0.0.0', port=3000)
137129
app.run(host='127.0.0.1', port=3000)
138130
```
139131

140-
## Best practices
141-
142-
**Service design**:
143-
- Bind to `0.0.0.0` to make accessible
144-
- Add authentication (don't rely on URL secrecy)
145-
- Include health check endpoints
146-
- Handle CORS if accessed from browsers
132+
### Production Errors
147133

148-
**Cleanup**:
149-
- Unexpose ports when done: `await sandbox.unexposePort(port)`
150-
- Stop processes: `await sandbox.killAllProcesses()`
134+
For custom domain issues, see [Production Deployment troubleshooting](/sandbox/guides/production-deployment/#troubleshooting).
151135

152-
## Local development
136+
### Local Development
153137

154138
:::caution[Local development only]
155139
When using `wrangler dev`, you must expose ports in your Dockerfile:
@@ -167,7 +151,9 @@ Without `EXPOSE`, you'll see: `connect(): Connection refused: container port not
167151
This is **only required for local development**. In production, all container ports are automatically accessible.
168152
:::
169153

170-
## Related resources
154+
## Related Resources
171155

172-
- [Ports API reference](/sandbox/api/ports/) - Complete port exposure API
173-
- [Expose services guide](/sandbox/guides/expose-services/) - Practical patterns
156+
- [Production Deployment](/sandbox/guides/production-deployment/) - Set up custom domains for production
157+
- [Expose Services](/sandbox/guides/expose-services/) - Practical patterns for exposing ports
158+
- [Ports API](/sandbox/api/ports/) - Complete API reference
159+
- [Security Model](/sandbox/concepts/security/) - Security best practices

src/content/docs/sandbox/get-started.mdx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,11 @@ Visit your Worker URL (shown in deploy output):
151151
curl https://my-sandbox.YOUR_SUBDOMAIN.workers.dev/run
152152
```
153153

154-
Your sandbox is now deployed.
154+
Your sandbox is now deployed and can execute code in isolated containers.
155+
156+
:::note[Preview URLs require custom domain]
157+
If you plan to expose ports from sandboxes (using `exposePort()` for preview URLs), you will need to set up a custom domain with wildcard DNS routing. The `.workers.dev` domain does not support the subdomain patterns required for preview URLs. See [Production Deployment](/sandbox/guides/production-deployment/) when you are ready to expose services.
158+
:::
155159

156160
## Understanding the configuration
157161

@@ -201,4 +205,5 @@ Now that you have a working sandbox, explore more capabilities:
201205
- [Execute commands](/sandbox/guides/execute-commands/) - Run shell commands and stream output
202206
- [Manage files](/sandbox/guides/manage-files/) - Work with files and directories
203207
- [Expose services](/sandbox/guides/expose-services/) - Get public URLs for services running in your sandbox
208+
- [Production Deployment](/sandbox/guides/production-deployment/) - Set up custom domains for preview URLs
204209
- [API reference](/sandbox/api/) - Complete API documentation

src/content/docs/sandbox/guides/expose-services.mdx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ sidebar:
77

88
import { TypeScriptExample } from "~/components";
99

10+
:::note[Production requires custom domain]
11+
Preview URLs require a custom domain with wildcard DNS routing in production. See [Production Deployment](/sandbox/guides/production-deployment/) for setup instructions.
12+
:::
13+
1014
This guide shows you how to expose services running in your sandbox to the internet via preview URLs.
1115

1216
## When to expose ports
@@ -40,7 +44,8 @@ const exposed = await sandbox.exposePort(8000);
4044
4145
// 4. Preview URL is now available (public by default)
4246
console.log('Server accessible at:', exposed.exposedAt);
43-
// Returns: https://abc123-8000.sandbox.workers.dev
47+
// Production: https://8000-abc123.example.com
48+
// Local dev: http://localhost:8787/...
4449
4550
// 5. Handle preview URL requests in your Worker
4651
export default {
@@ -255,12 +260,14 @@ if (!ports.some(p => p.port === 8080)) {
255260
```
256261
</TypeScriptExample>
257262

258-
## Preview URL format
263+
## Preview URL Format
264+
265+
**Production**: `https://{port}-{sandbox-id}.yourdomain.com`
259266

260-
Preview URLs follow the pattern `https://{sandbox-id}-{port}.sandbox.workers.dev`:
267+
- Port 8080: `https://8080-abc123.example.com`
268+
- Port 5173: `https://5173-abc123.example.com`
261269

262-
- Port 8080: `https://abc123-8080.sandbox.workers.dev`
263-
- Port 5173: `https://abc123-5173.sandbox.workers.dev`
270+
**Local development**: `http://localhost:8787/...`
264271

265272
**Note**: Port 3000 is reserved for the internal Bun server and cannot be exposed.
266273

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
title: Deploy to Production
3+
pcx_content_type: how-to
4+
sidebar:
5+
order: 10
6+
---
7+
8+
import { WranglerConfig } from "~/components";
9+
10+
:::note[Only required for preview URLs]
11+
Custom domain setup is ONLY needed if you use `exposePort()` to expose services from sandboxes. If your application does not expose ports, you can deploy to `.workers.dev` without this configuration.
12+
:::
13+
14+
Deploy your Sandbox SDK application to production with preview URL support. Preview URLs require wildcard DNS routing because they generate unique subdomains for each exposed port: `https://8080-abc123.yourdomain.com`.
15+
16+
The `.workers.dev` domain does not support wildcard subdomains, so production deployments that use preview URLs need a custom domain.
17+
18+
## Prerequisites
19+
20+
- Active Cloudflare zone with a domain
21+
- Worker that uses `exposePort()`
22+
- [Wrangler CLI](/workers/wrangler/install-and-update/) installed
23+
24+
## Setup
25+
26+
### Create Wildcard DNS Record
27+
28+
In the Cloudflare dashboard, go to your domain and create an A record:
29+
30+
- **Type**: A
31+
- **Name**: * (wildcard)
32+
- **IPv4 address**: 192.0.2.0
33+
- **Proxy status**: Proxied (orange cloud)
34+
35+
This routes all subdomains through Cloudflare's proxy. The IP address `192.0.2.0` is a documentation address (RFC 5737) that Cloudflare recognizes when proxied.
36+
37+
### Configure Worker Routes
38+
39+
Add a wildcard route to your Wrangler configuration:
40+
41+
<WranglerConfig>
42+
```toml
43+
name = "my-sandbox-app"
44+
main = "src/index.ts"
45+
compatibility_date = "$today"
46+
47+
[[routes]]
48+
pattern = "*.yourdomain.com/*"
49+
zone_name = "yourdomain.com"
50+
```
51+
</WranglerConfig>
52+
53+
Replace `yourdomain.com` with your actual domain. This routes all subdomain requests to your Worker and enables Cloudflare to provision SSL certificates automatically.
54+
55+
### Deploy
56+
57+
Deploy your Worker:
58+
59+
```sh
60+
npx wrangler deploy
61+
```
62+
63+
## Verify
64+
65+
Test that preview URLs work:
66+
67+
```typescript
68+
const sandbox = getSandbox(env.Sandbox, 'test-sandbox');
69+
await sandbox.startProcess('python -m http.server 8080');
70+
const exposed = await sandbox.exposePort(8080);
71+
72+
console.log(exposed.exposedAt);
73+
// https://8080-test-sandbox.yourdomain.com
74+
```
75+
76+
Visit the URL in your browser to confirm your service is accessible.
77+
78+
## Troubleshooting
79+
80+
- **CustomDomainRequiredError**: Verify your Worker is not deployed to `.workers.dev` and that the wildcard DNS record and route are configured correctly.
81+
- **SSL/TLS errors**: Wait a few minutes for certificate provisioning. Verify the DNS record is proxied and SSL/TLS mode is set to "Full" or "Full (strict)" in your dashboard.
82+
- **Preview URL not resolving**: Confirm the wildcard DNS record exists and is proxied. Wait 30-60 seconds for DNS propagation.
83+
- **Port not accessible**: Ensure your service binds to `0.0.0.0` (not `localhost`) and that `proxyToSandbox()` is called first in your Worker's fetch handler.
84+
85+
For detailed troubleshooting, see the [Workers routing documentation](/workers/configuration/routing/).
86+
87+
## Related Resources
88+
89+
- [Preview URLs](/sandbox/concepts/preview-urls/) - How preview URLs work
90+
- [Expose Services](/sandbox/guides/expose-services/) - Patterns for exposing ports
91+
- [Workers Routing](/workers/configuration/routing/) - Advanced routing configuration
92+
- [Cloudflare DNS](/dns/) - DNS management

0 commit comments

Comments
 (0)