From 389eab2b4d51056314236784174c65c91e9fd84b Mon Sep 17 00:00:00 2001 From: "ask-bonk[bot]" Date: Mon, 16 Feb 2026 23:29:21 +0000 Subject: [PATCH 1/7] Sandbox SDK docs: 10 improvements done Co-authored-by: irvinebroque --- .../docs/sandbox/concepts/preview-urls.mdx | 35 +++++-- .../sandbox/guides/production-deployment.mdx | 92 ++++++++++++++++--- 2 files changed, 106 insertions(+), 21 deletions(-) diff --git a/src/content/docs/sandbox/concepts/preview-urls.mdx b/src/content/docs/sandbox/concepts/preview-urls.mdx index 8dc2edad6f6..89004329885 100644 --- a/src/content/docs/sandbox/concepts/preview-urls.mdx +++ b/src/content/docs/sandbox/concepts/preview-urls.mdx @@ -50,20 +50,22 @@ URLs with auto-generated tokens change when you unexpose and re-expose a port. For production deployments or shared URLs, specify a custom token to maintain consistency across container restarts: ```typescript -const stable = await sandbox.exposePort(8000, { - hostname, - token: 'api-v1' +const stable = await sandbox.exposePort(8000, { + hostname, + token: "api-v1", }); // https://8000-sandbox-id-api-v1.yourdomain.com // Same URL every time ✓ ``` **Token requirements:** + - 1-16 characters long -- Lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_) only +- Lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (\_) only - Must be unique within each sandbox **Use cases for custom tokens:** + - Production APIs with stable endpoints - Sharing demo URLs with external users - Documentation with consistent examples @@ -77,7 +79,7 @@ Preview URLs extract the sandbox ID from the hostname to route requests. Since h ```typescript // Problem scenario -const sandbox = getSandbox(env.Sandbox, 'MyProject-123'); +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 @@ -87,8 +89,8 @@ await sandbox.exposePort(8080, { hostname }); **The solution**: Use `normalizeId: true` to lowercase IDs when creating sandboxes: ```typescript -const sandbox = getSandbox(env.Sandbox, 'MyProject-123', { - 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 @@ -120,7 +122,9 @@ export default { }; ``` -Requests flow: Browser → Your Worker → Durable Object (sandbox) → Your Service. +**Request flow**: Browser → HTTPS → Cloudflare edge (TLS termination) → Worker → Durable Object → HTTP → Container + +Cloudflare terminates TLS at the edge. Your service inside the container receives plain HTTP requests over an internal connection -- no TLS is needed because the transport between the Durable Object and the container VM is already secure. Your service should bind on HTTP, not HTTPS. ## Multiple Ports @@ -155,6 +159,7 @@ const admin = await sandbox.exposePort(3001, { hostname, name: "admin" }); - Custom protocols (must wrap in HTTP) - Ports outside range 1024-65535 - Port 3000 (used internally by the SDK) +- HTTPS servers inside the container (the internal connection uses HTTP -- Cloudflare handles TLS at the edge, so your service should serve plain HTTP on the exposed port) ## WebSocket Support @@ -191,7 +196,7 @@ Preview URLs are publicly accessible by default, but require a valid access toke **Built-in security**: - **Token-based access** - Each exposed port gets a unique token in the URL (for example, `https://8080-sandbox-abc123token456.yourdomain.com`) -- **HTTPS in production** - All traffic is encrypted with TLS. Certificates are provisioned automatically for first-level wildcards (`*.yourdomain.com`). If your worker runs on a subdomain, see the [TLS note in Production Deployment](/sandbox/guides/production-deployment/). +- **HTTPS in production** - All traffic between the browser and Cloudflare is encrypted with TLS. If your Worker runs on the apex domain, Cloudflare's Universal SSL covers `*.yourdomain.com` automatically. If your Worker runs on a subdomain (for example, `sandbox.yourdomain.com`), you need a certificate covering `*.sandbox.yourdomain.com` -- Universal SSL does not cover second-level wildcards. Refer to [Production Deployment](/sandbox/guides/production-deployment/) for options including Advanced Certificate Manager ($10/month). - **Unpredictable URLs** - Auto-generated tokens are randomly generated and difficult to guess - **Token collision prevention** - Custom tokens are validated to ensure uniqueness within each sandbox @@ -236,6 +241,18 @@ app.run((host = "0.0.0.0"), (port = 3000)); app.run((host = "127.0.0.1"), (port = 3000)); ``` +### Container HTTPS Error + +If you see: `Connecting to a container using HTTPS is not currently supported; use HTTP instead` + +Your service inside the sandbox is serving HTTPS. Configure it to serve HTTP instead. Cloudflare handles TLS between the browser and the edge -- the connection from the Durable Object to your container is internal and already secure. + +Common causes: + +- Node.js using `https.createServer()` instead of `http.createServer()` +- A framework started with an HTTPS flag (for example, `--experimental-https` in Next.js) +- A framework redirecting HTTP to HTTPS (configure it to trust the proxy -- for example, `app.set('trust proxy', 1)` in Express) + ### Production Errors For custom domain issues, see [Production Deployment troubleshooting](/sandbox/guides/production-deployment/#troubleshooting). diff --git a/src/content/docs/sandbox/guides/production-deployment.mdx b/src/content/docs/sandbox/guides/production-deployment.mdx index f112c4d46cc..495dbcca82e 100644 --- a/src/content/docs/sandbox/guides/production-deployment.mdx +++ b/src/content/docs/sandbox/guides/production-deployment.mdx @@ -6,7 +6,7 @@ sidebar: description: Set up custom domains for preview URLs in production. --- -import { WranglerConfig } from "~/components"; +import { Tabs, TabItem, WranglerConfig } from "~/components"; :::note[Only required for preview URLs] 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. @@ -20,10 +20,23 @@ The `.workers.dev` domain does not support wildcard subdomains, so production de If your worker runs on a subdomain (for example, `sandbox.yourdomain.com`), preview URLs become second-level wildcards like `*.sandbox.yourdomain.com`. Cloudflare's Universal SSL only covers first-level wildcards (`*.yourdomain.com`), so you need a certificate covering `*.sandbox.yourdomain.com`. Without it, preview URLs will fail with TLS handshake errors. You have three options: + - **Deploy on the apex domain** (`yourdomain.com`) so preview URLs stay at the first level (`*.yourdomain.com`), which Universal SSL covers automatically. This is the simplest option. - **Use [Advanced Certificate Manager](/ssl/edge-certificates/advanced-certificate-manager/)** ($10/month) to provision a certificate for `*.sandbox.yourdomain.com` through the Cloudflare dashboard. -- **Upload a custom certificate** from a provider like [Let's Encrypt](https://letsencrypt.org/) (free). Generate a wildcard certificate for `*.sandbox.yourdomain.com` using the DNS-01 challenge, then upload it via the Cloudflare dashboard under **SSL/TLS > Edge Certificates > [Custom Certificates](/ssl/edge-certificates/custom-certificates/)**. You will need to renew it before expiry. -::: +- **Upload a custom certificate** from a provider like [Let's Encrypt](https://letsencrypt.org/) (free). Generate a wildcard certificate for `*.sandbox.yourdomain.com` using the DNS-01 challenge, then upload it via the Cloudflare dashboard under **SSL/TLS** > **Edge Certificates** > **[Custom Certificates](/ssl/edge-certificates/custom-certificates/)**. You will need to renew it before expiry. + ::: + +## How preview URL requests flow + +When a browser visits a preview URL like `https://8080-my-sandbox-abc123.yourdomain.com`: + +1. **DNS**: The wildcard record (`*.yourdomain.com`) resolves the hostname to Cloudflare's proxy. +2. **TLS**: Cloudflare terminates TLS at the edge using your domain's certificate. The browser gets a secure HTTPS connection. +3. **Worker route**: The wildcard route (`*.yourdomain.com/*`) matches the request and sends it to your Worker. +4. **`proxyToSandbox()`**: Your Worker calls `proxyToSandbox()`, which extracts the sandbox ID and port from the hostname and routes the request to the correct Durable Object. +5. **Container**: The Durable Object forwards the request to the service inside your container over an internal connection (HTTP -- TLS is not needed because the transport is already secure within Cloudflare's network). + +Each step can fail independently: DNS can resolve but TLS can fail (certificate does not cover the hostname), or TLS can succeed but the route does not match (request reaches the wrong Worker or no Worker at all). ## Prerequisites @@ -35,18 +48,45 @@ You have three options: ### Create Wildcard DNS Record -In the Cloudflare dashboard, go to your domain and create an A record: +In the Cloudflare dashboard, go to your domain and create an A record. The record name depends on whether your Worker runs on the apex domain or a subdomain. + + + + +If your Worker runs on the apex domain (`yourdomain.com`), create a wildcard record for `*`: - **Type**: A -- **Name**: * (wildcard) -- **IPv4 address**: 192.0.2.0 +- **Name**: `*` (wildcard) +- **IPv4 address**: `192.0.2.0` - **Proxy status**: Proxied (orange cloud) -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. +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. Preview URLs will look like `https://8080-sandbox-id-token.yourdomain.com`. + + + + +If your Worker runs on a subdomain (for example, `sandbox.yourdomain.com`), create a wildcard record for `*.sandbox`: + +- **Type**: A +- **Name**: `*.sandbox` (where `sandbox` matches your Worker's subdomain) +- **IPv4 address**: `192.0.2.0` +- **Proxy status**: Proxied (orange cloud) + +This routes all sub-subdomains of `sandbox.yourdomain.com` through Cloudflare's proxy. Preview URLs will look like `https://8080-sandbox-id-token.sandbox.yourdomain.com`. + +:::caution[TLS certificate required] +You also need a TLS certificate covering `*.sandbox.yourdomain.com`. Universal SSL does not cover second-level wildcards. Refer to the [TLS caution at the top of this page](#subdomain-depth-matters-for-tls), or use [Advanced Certificate Manager](/ssl/edge-certificates/advanced-certificate-manager/) ($10/month) to order one through the dashboard. +::: + + + ### Configure Worker Routes -Add a wildcard route to your Wrangler configuration: +Add a wildcard route to your Wrangler configuration. The route pattern must match the domain or subdomain your Worker runs on. + + + ```jsonc @@ -65,7 +105,32 @@ Add a wildcard route to your Wrangler configuration: ``` -Replace `yourdomain.com` with your actual domain. This routes all subdomain requests to your Worker and enables Cloudflare to provision SSL certificates automatically. +Replace `yourdomain.com` with your actual domain. + + + + + +```jsonc +{ + "$schema": "./node_modules/wrangler/config-schema.json", + "name": "my-sandbox-app", + "main": "src/index.ts", + "compatibility_date": "$today", + "routes": [ + { + "pattern": "*.sandbox.yourdomain.com/*", + "zone_name": "yourdomain.com" + } + ] +} +``` + + +Replace `sandbox.yourdomain.com` with your actual subdomain and `yourdomain.com` with your zone name. + + + ### Deploy @@ -83,8 +148,8 @@ Test that preview URLs work: // Extract hostname from request const { hostname } = new URL(request.url); -const sandbox = getSandbox(env.Sandbox, 'test-sandbox'); -await sandbox.startProcess('python -m http.server 8080'); +const sandbox = getSandbox(env.Sandbox, "test-sandbox"); +await sandbox.startProcess("python -m http.server 8080"); const exposed = await sandbox.exposePort(8080, { hostname }); console.log(exposed.exposedAt); @@ -96,9 +161,12 @@ Visit the URL in your browser to confirm your service is accessible. ## Troubleshooting - **CustomDomainRequiredError**: Verify your Worker is not deployed to `.workers.dev` and that the wildcard DNS record and route are configured correctly. -- **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. If your worker is on a subdomain (for example, `sandbox.yourdomain.com`), Universal SSL won't cover the second-level wildcard `*.sandbox.yourdomain.com` — see the [TLS caution](#subdomain-depth-matters-for-tls) at the top of this page for options. +- **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. If your worker is on a subdomain (for example, `sandbox.yourdomain.com`), Universal SSL will not cover the second-level wildcard `*.sandbox.yourdomain.com` -- see the [TLS caution](#subdomain-depth-matters-for-tls) at the top of this page for options. - **Preview URL not resolving**: Confirm the wildcard DNS record exists and is proxied. Wait 30-60 seconds for DNS propagation. - **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. +- **"Connecting to a container using HTTPS is not currently supported"**: Your service inside the sandbox is using HTTPS (for example, Node.js `https.createServer()` or a framework with `--https` enabled). Configure it to serve plain HTTP instead. Cloudflare terminates TLS at the edge, so the browser connects over HTTPS, but the internal connection to your container uses HTTP. This is the same model as running behind any reverse proxy. If your framework checks the protocol and redirects HTTP to HTTPS, configure it to trust the proxy (for example, `app.set('trust proxy', 1)` in Express). +- **Error 1016 (Origin DNS error)**: Cloudflare cannot resolve the origin for the requested hostname. Verify your wildcard DNS record exists and is proxied (orange cloud). If your Worker runs on a subdomain like `sandbox.yourdomain.com`, the DNS record name must be `*.sandbox`, not `*`. Wait 30-60 seconds for propagation after creating the record. +- **Error 522 (Connection timed out)**: Cloudflare timed out reaching the origin. This often happens temporarily after creating or changing DNS records and routes. Wait 60 seconds and retry. If it persists, verify `proxyToSandbox()` is called first in your Worker's fetch handler -- if it is not handling the request, Cloudflare tries to reach the `192.0.2.0` origin, which times out. For detailed troubleshooting, see the [Workers routing documentation](/workers/configuration/routing/). From d9df6db8873421916ff72c63fc384a10265b5f8e Mon Sep 17 00:00:00 2001 From: "ask-bonk[bot]" Date: Mon, 16 Feb 2026 23:42:18 +0000 Subject: [PATCH 2/7] [Sandbox] Remove ACM pricing, omit specific error text, fix exposePort API usage --- src/content/docs/sandbox/concepts/preview-urls.mdx | 6 ++---- src/content/docs/sandbox/guides/production-deployment.mdx | 8 ++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/content/docs/sandbox/concepts/preview-urls.mdx b/src/content/docs/sandbox/concepts/preview-urls.mdx index 89004329885..4a30a88bb82 100644 --- a/src/content/docs/sandbox/concepts/preview-urls.mdx +++ b/src/content/docs/sandbox/concepts/preview-urls.mdx @@ -196,7 +196,7 @@ Preview URLs are publicly accessible by default, but require a valid access toke **Built-in security**: - **Token-based access** - Each exposed port gets a unique token in the URL (for example, `https://8080-sandbox-abc123token456.yourdomain.com`) -- **HTTPS in production** - All traffic between the browser and Cloudflare is encrypted with TLS. If your Worker runs on the apex domain, Cloudflare's Universal SSL covers `*.yourdomain.com` automatically. If your Worker runs on a subdomain (for example, `sandbox.yourdomain.com`), you need a certificate covering `*.sandbox.yourdomain.com` -- Universal SSL does not cover second-level wildcards. Refer to [Production Deployment](/sandbox/guides/production-deployment/) for options including Advanced Certificate Manager ($10/month). +- **HTTPS in production** - All traffic between the browser and Cloudflare is encrypted with TLS. If your Worker runs on the apex domain, Cloudflare's Universal SSL covers `*.yourdomain.com` automatically. If your Worker runs on a subdomain (for example, `sandbox.yourdomain.com`), you need a certificate covering `*.sandbox.yourdomain.com` -- Universal SSL does not cover second-level wildcards. Refer to [Production Deployment](/sandbox/guides/production-deployment/) for options including [Advanced Certificate Manager](/ssl/edge-certificates/advanced-certificate-manager/). - **Unpredictable URLs** - Auto-generated tokens are randomly generated and difficult to guess - **Token collision prevention** - Custom tokens are validated to ensure uniqueness within each sandbox @@ -243,9 +243,7 @@ app.run((host = "127.0.0.1"), (port = 3000)); ### Container HTTPS Error -If you see: `Connecting to a container using HTTPS is not currently supported; use HTTP instead` - -Your service inside the sandbox is serving HTTPS. Configure it to serve HTTP instead. Cloudflare handles TLS between the browser and the edge -- the connection from the Durable Object to your container is internal and already secure. +If you receive an error when connecting to your container, your service inside the sandbox may be serving HTTPS. Configure it to serve HTTP instead. Cloudflare handles TLS between the browser and the edge -- the connection from the Durable Object to your container is internal and already secure. Common causes: diff --git a/src/content/docs/sandbox/guides/production-deployment.mdx b/src/content/docs/sandbox/guides/production-deployment.mdx index 495dbcca82e..ee521f743a9 100644 --- a/src/content/docs/sandbox/guides/production-deployment.mdx +++ b/src/content/docs/sandbox/guides/production-deployment.mdx @@ -22,7 +22,7 @@ If your worker runs on a subdomain (for example, `sandbox.yourdomain.com`), prev You have three options: - **Deploy on the apex domain** (`yourdomain.com`) so preview URLs stay at the first level (`*.yourdomain.com`), which Universal SSL covers automatically. This is the simplest option. -- **Use [Advanced Certificate Manager](/ssl/edge-certificates/advanced-certificate-manager/)** ($10/month) to provision a certificate for `*.sandbox.yourdomain.com` through the Cloudflare dashboard. +- **Use [Advanced Certificate Manager](/ssl/edge-certificates/advanced-certificate-manager/)** to provision a certificate for `*.sandbox.yourdomain.com` through the Cloudflare dashboard. - **Upload a custom certificate** from a provider like [Let's Encrypt](https://letsencrypt.org/) (free). Generate a wildcard certificate for `*.sandbox.yourdomain.com` using the DNS-01 challenge, then upload it via the Cloudflare dashboard under **SSL/TLS** > **Edge Certificates** > **[Custom Certificates](/ssl/edge-certificates/custom-certificates/)**. You will need to renew it before expiry. ::: @@ -75,7 +75,7 @@ If your Worker runs on a subdomain (for example, `sandbox.yourdomain.com`), crea This routes all sub-subdomains of `sandbox.yourdomain.com` through Cloudflare's proxy. Preview URLs will look like `https://8080-sandbox-id-token.sandbox.yourdomain.com`. :::caution[TLS certificate required] -You also need a TLS certificate covering `*.sandbox.yourdomain.com`. Universal SSL does not cover second-level wildcards. Refer to the [TLS caution at the top of this page](#subdomain-depth-matters-for-tls), or use [Advanced Certificate Manager](/ssl/edge-certificates/advanced-certificate-manager/) ($10/month) to order one through the dashboard. +You also need a TLS certificate covering `*.sandbox.yourdomain.com`. Universal SSL does not cover second-level wildcards. Refer to the [TLS caution at the top of this page](#subdomain-depth-matters-for-tls), or use [Advanced Certificate Manager](/ssl/edge-certificates/advanced-certificate-manager/) to order one through the dashboard. ::: @@ -152,7 +152,7 @@ const sandbox = getSandbox(env.Sandbox, "test-sandbox"); await sandbox.startProcess("python -m http.server 8080"); const exposed = await sandbox.exposePort(8080, { hostname }); -console.log(exposed.exposedAt); +console.log(exposed.url); // https://8080-test-sandbox.yourdomain.com ``` @@ -164,7 +164,7 @@ Visit the URL in your browser to confirm your service is accessible. - **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. If your worker is on a subdomain (for example, `sandbox.yourdomain.com`), Universal SSL will not cover the second-level wildcard `*.sandbox.yourdomain.com` -- see the [TLS caution](#subdomain-depth-matters-for-tls) at the top of this page for options. - **Preview URL not resolving**: Confirm the wildcard DNS record exists and is proxied. Wait 30-60 seconds for DNS propagation. - **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. -- **"Connecting to a container using HTTPS is not currently supported"**: Your service inside the sandbox is using HTTPS (for example, Node.js `https.createServer()` or a framework with `--https` enabled). Configure it to serve plain HTTP instead. Cloudflare terminates TLS at the edge, so the browser connects over HTTPS, but the internal connection to your container uses HTTP. This is the same model as running behind any reverse proxy. If your framework checks the protocol and redirects HTTP to HTTPS, configure it to trust the proxy (for example, `app.set('trust proxy', 1)` in Express). +- **Container HTTPS error**: Your service inside the sandbox is using HTTPS (for example, Node.js `https.createServer()` or a framework with `--https` enabled). Configure it to serve plain HTTP instead. Cloudflare terminates TLS at the edge, so the browser connects over HTTPS, but the internal connection to your container uses HTTP. This is the same model as running behind any reverse proxy. If your framework checks the protocol and redirects HTTP to HTTPS, configure it to trust the proxy (for example, `app.set('trust proxy', 1)` in Express). - **Error 1016 (Origin DNS error)**: Cloudflare cannot resolve the origin for the requested hostname. Verify your wildcard DNS record exists and is proxied (orange cloud). If your Worker runs on a subdomain like `sandbox.yourdomain.com`, the DNS record name must be `*.sandbox`, not `*`. Wait 30-60 seconds for propagation after creating the record. - **Error 522 (Connection timed out)**: Cloudflare timed out reaching the origin. This often happens temporarily after creating or changing DNS records and routes. Wait 60 seconds and retry. If it persists, verify `proxyToSandbox()` is called first in your Worker's fetch handler -- if it is not handling the request, Cloudflare tries to reach the `192.0.2.0` origin, which times out. From a04ac5e667e77632741ec98a9a1a525a0377ea4b Mon Sep 17 00:00:00 2001 From: "ask-bonk[bot]" Date: Mon, 16 Feb 2026 23:44:32 +0000 Subject: [PATCH 3/7] Link 522/1016 errors to Cloudflare docs Co-authored-by: irvinebroque --- src/content/docs/sandbox/guides/production-deployment.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/docs/sandbox/guides/production-deployment.mdx b/src/content/docs/sandbox/guides/production-deployment.mdx index ee521f743a9..95b8c720311 100644 --- a/src/content/docs/sandbox/guides/production-deployment.mdx +++ b/src/content/docs/sandbox/guides/production-deployment.mdx @@ -165,8 +165,8 @@ Visit the URL in your browser to confirm your service is accessible. - **Preview URL not resolving**: Confirm the wildcard DNS record exists and is proxied. Wait 30-60 seconds for DNS propagation. - **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. - **Container HTTPS error**: Your service inside the sandbox is using HTTPS (for example, Node.js `https.createServer()` or a framework with `--https` enabled). Configure it to serve plain HTTP instead. Cloudflare terminates TLS at the edge, so the browser connects over HTTPS, but the internal connection to your container uses HTTP. This is the same model as running behind any reverse proxy. If your framework checks the protocol and redirects HTTP to HTTPS, configure it to trust the proxy (for example, `app.set('trust proxy', 1)` in Express). -- **Error 1016 (Origin DNS error)**: Cloudflare cannot resolve the origin for the requested hostname. Verify your wildcard DNS record exists and is proxied (orange cloud). If your Worker runs on a subdomain like `sandbox.yourdomain.com`, the DNS record name must be `*.sandbox`, not `*`. Wait 30-60 seconds for propagation after creating the record. -- **Error 522 (Connection timed out)**: Cloudflare timed out reaching the origin. This often happens temporarily after creating or changing DNS records and routes. Wait 60 seconds and retry. If it persists, verify `proxyToSandbox()` is called first in your Worker's fetch handler -- if it is not handling the request, Cloudflare tries to reach the `192.0.2.0` origin, which times out. +- **[Error 1016](/support/troubleshooting/http-status-codes/cloudflare-1xxx-errors/error-1016/)**: Verify your wildcard DNS record exists and is proxied (orange cloud). If your Worker runs on a subdomain like `sandbox.yourdomain.com`, the DNS record name must be `*.sandbox`, not `*`. +- **[Error 522](/support/troubleshooting/http-status-codes/cloudflare-5xx-errors/error-522/)**: Verify `proxyToSandbox()` is called first in your Worker's fetch handler -- if it is not handling the request, Cloudflare tries to reach the `192.0.2.0` origin, which times out. For detailed troubleshooting, see the [Workers routing documentation](/workers/configuration/routing/). From 81aab53fd4b951f22a82a5b10882d38f2332ed86 Mon Sep 17 00:00:00 2001 From: "ask-bonk[bot]" Date: Mon, 16 Feb 2026 23:53:18 +0000 Subject: [PATCH 4/7] [Sandbox] Add HTTPS redirect troubleshooting for container binding Expand Container HTTPS Error troubleshooting in both production-deployment.mdx and preview-urls.mdx to cover the redirect-following mechanism where frameworks like Next.js construct https:// redirect URLs from X-Forwarded-Proto. Add debugging tip for redirect: manual, Next.js-specific subsection, and update the request flow steps to document X-Forwarded-Proto and redirect behavior. --- .../docs/sandbox/concepts/preview-urls.mdx | 15 +++++++++++++++ .../docs/sandbox/guides/production-deployment.mdx | 7 ++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/content/docs/sandbox/concepts/preview-urls.mdx b/src/content/docs/sandbox/concepts/preview-urls.mdx index 4a30a88bb82..851ad3c9773 100644 --- a/src/content/docs/sandbox/concepts/preview-urls.mdx +++ b/src/content/docs/sandbox/concepts/preview-urls.mdx @@ -250,6 +250,21 @@ Common causes: - Node.js using `https.createServer()` instead of `http.createServer()` - A framework started with an HTTPS flag (for example, `--experimental-https` in Next.js) - A framework redirecting HTTP to HTTPS (configure it to trust the proxy -- for example, `app.set('trust proxy', 1)` in Express) +- Auth middleware or framework middleware producing HTTPS redirect URLs. `proxyToSandbox()` sets `X-Forwarded-Proto: https` on requests to your container. Frameworks like Next.js read this header and construct internal URLs with `https://`. If your middleware redirects using these URLs, the `Location` header may contain an absolute `https://` URL. The container binding follows redirects by default and rejects the `https://` scheme. To fix, use relative paths in redirects, or set your auth library's base URL to the container's internal HTTP address (for example, `NEXTAUTH_URL=http://localhost:3001`). + +#### Next.js + +Next.js reads the `X-Forwarded-Proto` header and uses it to construct internal URLs. When running `next dev` behind a Sandbox preview URL: + +- Middleware receives `request.url` with `https://` protocol +- Auth libraries (NextAuth, Auth.js, Clerk) may construct redirect URLs using this protocol +- Server Actions that call `redirect()` trigger an internal self-fetch using the HTTPS protocol + +To fix: + +- Set your auth library's base URL to the container's internal address: `NEXTAUTH_URL=http://localhost:3001` or `AUTH_URL=http://localhost:3001` +- If using Server Actions with redirects, set `__NEXT_PRIVATE_ORIGIN=http://localhost:3001` as an environment variable in the container +- Use relative paths in middleware redirects instead of constructing URLs from `request.url` ### Production Errors diff --git a/src/content/docs/sandbox/guides/production-deployment.mdx b/src/content/docs/sandbox/guides/production-deployment.mdx index 95b8c720311..b784a825e56 100644 --- a/src/content/docs/sandbox/guides/production-deployment.mdx +++ b/src/content/docs/sandbox/guides/production-deployment.mdx @@ -33,8 +33,8 @@ When a browser visits a preview URL like `https://8080-my-sandbox-abc123.yourdom 1. **DNS**: The wildcard record (`*.yourdomain.com`) resolves the hostname to Cloudflare's proxy. 2. **TLS**: Cloudflare terminates TLS at the edge using your domain's certificate. The browser gets a secure HTTPS connection. 3. **Worker route**: The wildcard route (`*.yourdomain.com/*`) matches the request and sends it to your Worker. -4. **`proxyToSandbox()`**: Your Worker calls `proxyToSandbox()`, which extracts the sandbox ID and port from the hostname and routes the request to the correct Durable Object. -5. **Container**: The Durable Object forwards the request to the service inside your container over an internal connection (HTTP -- TLS is not needed because the transport is already secure within Cloudflare's network). +4. **`proxyToSandbox()`**: Your Worker calls `proxyToSandbox()`, which extracts the sandbox ID and port from the hostname, rewrites the URL to `http://localhost:{port}`, and forwards the request to the correct Durable Object. It sets `X-Forwarded-Proto: https` so your application can detect the original protocol. +5. **Container**: The Durable Object forwards the request to the service inside your container over an internal connection (HTTP -- TLS is not needed because the transport is already secure within Cloudflare's network). If your service responds with an HTTP redirect, the container binding follows it automatically. If the redirect `Location` header contains an `https://` URL, the request fails. To prevent this, ensure your application produces HTTP or relative redirect URLs, or pass `redirect: "manual"` in your Worker to handle redirects yourself. Each step can fail independently: DNS can resolve but TLS can fail (certificate does not cover the hostname), or TLS can succeed but the route does not match (request reaches the wrong Worker or no Worker at all). @@ -164,7 +164,8 @@ Visit the URL in your browser to confirm your service is accessible. - **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. If your worker is on a subdomain (for example, `sandbox.yourdomain.com`), Universal SSL will not cover the second-level wildcard `*.sandbox.yourdomain.com` -- see the [TLS caution](#subdomain-depth-matters-for-tls) at the top of this page for options. - **Preview URL not resolving**: Confirm the wildcard DNS record exists and is proxied. Wait 30-60 seconds for DNS propagation. - **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. -- **Container HTTPS error**: Your service inside the sandbox is using HTTPS (for example, Node.js `https.createServer()` or a framework with `--https` enabled). Configure it to serve plain HTTP instead. Cloudflare terminates TLS at the edge, so the browser connects over HTTPS, but the internal connection to your container uses HTTP. This is the same model as running behind any reverse proxy. If your framework checks the protocol and redirects HTTP to HTTPS, configure it to trust the proxy (for example, `app.set('trust proxy', 1)` in Express). +- **Container HTTPS error**: Your service inside the sandbox is using HTTPS (for example, Node.js `https.createServer()` or a framework with `--https` enabled). Configure it to serve plain HTTP instead. Cloudflare terminates TLS at the edge, so the browser connects over HTTPS, but the internal connection to your container uses HTTP. This is the same model as running behind any reverse proxy. If your framework checks the protocol and redirects HTTP to HTTPS, configure it to trust the proxy (for example, `app.set('trust proxy', 1)` in Express). This error also occurs when your application responds with an HTTP redirect to an `https://` URL. The container binding follows redirects by default. If your framework middleware or auth library constructs redirect URLs using the `X-Forwarded-Proto` header (which `proxyToSandbox()` sets to `https`), the `Location` header may contain an absolute `https://` URL that fails when followed. To debug, pass `redirect: "manual"` to your `containerFetch` call and log the `Location` header. To fix, use relative paths in redirects, or set your auth library's base URL to the container's internal HTTP address (for example, `NEXTAUTH_URL=http://localhost:3001` for NextAuth). +- **Debugging container HTTPS errors**: The container binding follows HTTP redirects by default. If your application redirects to an `https://` URL (for example, auth middleware redirecting to a sign-in page), the binding follows the redirect internally and fails. To debug, pass `redirect: "manual"` to your `containerFetch` call and log the response status and `Location` header to see what URL your application is producing. - **[Error 1016](/support/troubleshooting/http-status-codes/cloudflare-1xxx-errors/error-1016/)**: Verify your wildcard DNS record exists and is proxied (orange cloud). If your Worker runs on a subdomain like `sandbox.yourdomain.com`, the DNS record name must be `*.sandbox`, not `*`. - **[Error 522](/support/troubleshooting/http-status-codes/cloudflare-5xx-errors/error-522/)**: Verify `proxyToSandbox()` is called first in your Worker's fetch handler -- if it is not handling the request, Cloudflare tries to reach the `192.0.2.0` origin, which times out. From a90ee497035ac18dfaa4ea6d77bfda42a499d941 Mon Sep 17 00:00:00 2001 From: Brendan Irvine-Broque Date: Mon, 16 Feb 2026 15:57:17 -0800 Subject: [PATCH 5/7] Apply suggestion from @irvinebroque --- src/content/docs/sandbox/guides/production-deployment.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/sandbox/guides/production-deployment.mdx b/src/content/docs/sandbox/guides/production-deployment.mdx index b784a825e56..fbacaa6749b 100644 --- a/src/content/docs/sandbox/guides/production-deployment.mdx +++ b/src/content/docs/sandbox/guides/production-deployment.mdx @@ -164,7 +164,7 @@ Visit the URL in your browser to confirm your service is accessible. - **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. If your worker is on a subdomain (for example, `sandbox.yourdomain.com`), Universal SSL will not cover the second-level wildcard `*.sandbox.yourdomain.com` -- see the [TLS caution](#subdomain-depth-matters-for-tls) at the top of this page for options. - **Preview URL not resolving**: Confirm the wildcard DNS record exists and is proxied. Wait 30-60 seconds for DNS propagation. - **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. -- **Container HTTPS error**: Your service inside the sandbox is using HTTPS (for example, Node.js `https.createServer()` or a framework with `--https` enabled). Configure it to serve plain HTTP instead. Cloudflare terminates TLS at the edge, so the browser connects over HTTPS, but the internal connection to your container uses HTTP. This is the same model as running behind any reverse proxy. If your framework checks the protocol and redirects HTTP to HTTPS, configure it to trust the proxy (for example, `app.set('trust proxy', 1)` in Express). This error also occurs when your application responds with an HTTP redirect to an `https://` URL. The container binding follows redirects by default. If your framework middleware or auth library constructs redirect URLs using the `X-Forwarded-Proto` header (which `proxyToSandbox()` sets to `https`), the `Location` header may contain an absolute `https://` URL that fails when followed. To debug, pass `redirect: "manual"` to your `containerFetch` call and log the `Location` header. To fix, use relative paths in redirects, or set your auth library's base URL to the container's internal HTTP address (for example, `NEXTAUTH_URL=http://localhost:3001` for NextAuth). +- **Container HTTPS error**: Your service inside the sandbox is using HTTPS (for example, Node.js `https.createServer()` or a framework with `--https` enabled). Configure it to serve plain HTTP instead. Cloudflare terminates TLS at the edge, so the browser connects over HTTPS, but the internal connection to your container uses HTTP. If your framework checks the protocol and redirects HTTP to HTTPS, configure it to trust the proxy (for example, `app.set('trust proxy', 1)` in Express). This error also occurs when your application responds with an HTTP redirect to an `https://` URL. The container binding follows redirects by default. If your framework middleware or auth library constructs redirect URLs using the `X-Forwarded-Proto` header (which `proxyToSandbox()` sets to `https`), the `Location` header may contain an absolute `https://` URL that fails when followed. To debug, pass `redirect: "manual"` to your `containerFetch` call and log the `Location` header. To fix, use relative paths in redirects, or set your auth library's base URL to the container's internal HTTP address (for example, `NEXTAUTH_URL=http://localhost:3001` for NextAuth). - **Debugging container HTTPS errors**: The container binding follows HTTP redirects by default. If your application redirects to an `https://` URL (for example, auth middleware redirecting to a sign-in page), the binding follows the redirect internally and fails. To debug, pass `redirect: "manual"` to your `containerFetch` call and log the response status and `Location` header to see what URL your application is producing. - **[Error 1016](/support/troubleshooting/http-status-codes/cloudflare-1xxx-errors/error-1016/)**: Verify your wildcard DNS record exists and is proxied (orange cloud). If your Worker runs on a subdomain like `sandbox.yourdomain.com`, the DNS record name must be `*.sandbox`, not `*`. - **[Error 522](/support/troubleshooting/http-status-codes/cloudflare-5xx-errors/error-522/)**: Verify `proxyToSandbox()` is called first in your Worker's fetch handler -- if it is not handling the request, Cloudflare tries to reach the `192.0.2.0` origin, which times out. From 6d85436401c0274ef4d4fcc16b727d850f6df18c Mon Sep 17 00:00:00 2001 From: "ask-bonk[bot]" Date: Tue, 17 Feb 2026 00:21:24 +0000 Subject: [PATCH 6/7] [Sandbox] Add mermaid sequence diagram to preview URL request flow section --- .../sandbox/guides/production-deployment.mdx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/content/docs/sandbox/guides/production-deployment.mdx b/src/content/docs/sandbox/guides/production-deployment.mdx index fbacaa6749b..c2e7a9577e3 100644 --- a/src/content/docs/sandbox/guides/production-deployment.mdx +++ b/src/content/docs/sandbox/guides/production-deployment.mdx @@ -36,6 +36,41 @@ When a browser visits a preview URL like `https://8080-my-sandbox-abc123.yourdom 4. **`proxyToSandbox()`**: Your Worker calls `proxyToSandbox()`, which extracts the sandbox ID and port from the hostname, rewrites the URL to `http://localhost:{port}`, and forwards the request to the correct Durable Object. It sets `X-Forwarded-Proto: https` so your application can detect the original protocol. 5. **Container**: The Durable Object forwards the request to the service inside your container over an internal connection (HTTP -- TLS is not needed because the transport is already secure within Cloudflare's network). If your service responds with an HTTP redirect, the container binding follows it automatically. If the redirect `Location` header contains an `https://` URL, the request fails. To prevent this, ensure your application produces HTTP or relative redirect URLs, or pass `redirect: "manual"` in your Worker to handle redirects yourself. +```mermaid +sequenceDiagram + participant Browser + participant DNS + participant Edge as Cloudflare Edge (TLS) + participant Route as Worker Route + participant Worker as Worker (proxyToSandbox) + participant DO as Durable Object + participant Container as Container (HTTP) + + Browser->>DNS: Resolve 8080-my-sandbox-abc123.yourdomain.com + DNS-->>Browser: *.yourdomain.com → Cloudflare proxy IP + + Browser->>Edge: HTTPS request + Edge->>Edge: Terminate TLS using domain certificate + Edge-->>Browser: Secure HTTPS connection established + + Edge->>Route: Forward request + Route->>Route: Match *.yourdomain.com/* + Route->>Worker: Dispatch to Worker + + Worker->>Worker: Extract sandbox ID & port from hostname + Worker->>Worker: Rewrite URL to http://localhost:{port} + Worker->>Worker: Set X-Forwarded-Proto: https + Worker->>DO: Forward request to Durable Object + + DO->>Container: HTTP request (internal, already secure) + Container-->>DO: HTTP response + DO-->>Worker: Response + Worker-->>Edge: Response + Edge-->>Browser: HTTPS response + + Note over DO,Container: If container responds with 302 Location: https://...
the binding follows the redirect and fails.
Use redirect: "manual" to debug. +``` + Each step can fail independently: DNS can resolve but TLS can fail (certificate does not cover the hostname), or TLS can succeed but the route does not match (request reaches the wrong Worker or no Worker at all). ## Prerequisites From b5d2bbefef3ceecbc3ba16d7702c0cd5e8796b62 Mon Sep 17 00:00:00 2001 From: "ask-bonk[bot]" Date: Tue, 17 Feb 2026 00:25:52 +0000 Subject: [PATCH 7/7] Replace nested tabs with H4 headings Co-authored-by: irvinebroque --- .../sandbox/guides/production-deployment.mdx | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/content/docs/sandbox/guides/production-deployment.mdx b/src/content/docs/sandbox/guides/production-deployment.mdx index c2e7a9577e3..fed586120fd 100644 --- a/src/content/docs/sandbox/guides/production-deployment.mdx +++ b/src/content/docs/sandbox/guides/production-deployment.mdx @@ -6,7 +6,7 @@ sidebar: description: Set up custom domains for preview URLs in production. --- -import { Tabs, TabItem, WranglerConfig } from "~/components"; +import { WranglerConfig } from "~/components"; :::note[Only required for preview URLs] 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. @@ -85,8 +85,7 @@ Each step can fail independently: DNS can resolve but TLS can fail (certificate In the Cloudflare dashboard, go to your domain and create an A record. The record name depends on whether your Worker runs on the apex domain or a subdomain. - - +#### Apex domain If your Worker runs on the apex domain (`yourdomain.com`), create a wildcard record for `*`: @@ -97,8 +96,7 @@ If your Worker runs on the apex domain (`yourdomain.com`), create a wildcard rec 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. Preview URLs will look like `https://8080-sandbox-id-token.yourdomain.com`. - - +#### Subdomain If your Worker runs on a subdomain (for example, `sandbox.yourdomain.com`), create a wildcard record for `*.sandbox`: @@ -113,15 +111,11 @@ This routes all sub-subdomains of `sandbox.yourdomain.com` through Cloudflare's You also need a TLS certificate covering `*.sandbox.yourdomain.com`. Universal SSL does not cover second-level wildcards. Refer to the [TLS caution at the top of this page](#subdomain-depth-matters-for-tls), or use [Advanced Certificate Manager](/ssl/edge-certificates/advanced-certificate-manager/) to order one through the dashboard. ::: - - - ### Configure Worker Routes Add a wildcard route to your Wrangler configuration. The route pattern must match the domain or subdomain your Worker runs on. - - +#### Apex domain ```jsonc @@ -142,8 +136,7 @@ Add a wildcard route to your Wrangler configuration. The route pattern must matc Replace `yourdomain.com` with your actual domain. - - +#### Subdomain ```jsonc @@ -164,9 +157,6 @@ Replace `yourdomain.com` with your actual domain. Replace `sandbox.yourdomain.com` with your actual subdomain and `yourdomain.com` with your zone name. - - - ### Deploy Deploy your Worker: