How Do I Give a Port in a Cloud Sandbox an HTTPS URL?
Updated October 27, 2026 .md
Short answer: Start your server on a port, then run sbx host
At a glance#
| You want | Run | What a request without the token gets |
|---|---|---|
| A link only you and the people you send it to can open | sbx host $id 8000 |
401 Access token required |
| A URL anyone can open | sbx host $id 8000 --public |
200 |
| A dev server that listens on 127.0.0.1 only | sbx host $id 5173 --localhost |
Depends on --public |
| To see what's hosted | sbx host list $id |
|
| To take a URL down | sbx host hide $id 8000 |
404 Port not hosted |
Everything below was run on a small sandbox on 2026-10-27. Tokens are replaced with <token>.
Host a port#
Start a server on a known port, bound to 0.0.0.0, then host it.
id=sb_m0lu5n3z2r # your sandbox id from `sbx new`
sbx exec $id --detach "mkdir -p site && cd site && echo hello from the sandbox > index.html && python3 -m http.server 8000"
sbx host $id 8000 --title "Demo"https://frank-ready-marsh-8000.box.sandboxer.com?_token=<token>
Private URL: keep the _token secret. `sbx host sb_m0lu5n3z2r 8000 --public` makes it ungated.The hostname is the sandbox's subdomain plus the port. Running sbx host again for the same port returned the same URL and the same token. Inside the sandbox, the shorter host 8000 does the same thing for the sandbox it runs in.
What a private URL does without its token#
Without the token, the proxy answers itself. Your server never sees the request.
curl -si https://frank-ready-marsh-8000.box.sandboxer.com/ | head -1HTTP/2 401The body is a page titled "Access token required". A wrong token also gets 401.
With the token, the first response is a redirect that sets a cookie and drops the token from the address bar:
curl -si "https://frank-ready-marsh-8000.box.sandboxer.com?_token=<token>"HTTP/2 302
location: /
set-cookie: __sbx_auth=<cookie>; Path=/; Max-Age=2592000; HttpOnly; Secure; SameSite=LaxAfter that, the cookie alone is enough, so a browser can follow links and load assets normally. The cookie lasts 30 days. Treat the full URL and the cookie like a password.
Make it public, or private again#
sbx host $id 8000 --public
curl -s -w "\n%{http_code}\n" https://frank-ready-marsh-8000.box.sandboxer.com/hello from the sandbox
200sbx host $id 8000 --private gates the URL again with a new token. Switching between public and private mints a new token, so links and cookies from before stop working: in our re-test on 2026-10-27, the old private link answered 401 after a public-then-private switch, and the new one worked. (An earlier run that night, before a fix shipped, kept the same token.) Running sbx host again with the same access keeps the token, so to cut someone off, switch the port to public and back to private, or stop serving it with sbx host hide.
Apps that listen on localhost only#
Many dev servers (Vite, for example) bind to 127.0.0.1. The proxy connects to the sandbox's own address, so a normal host can't reach them:
sbx exec $id --detach "mkdir -p lh && cd lh && echo only on loopback > index.html && python3 -m http.server 5173 --bind 127.0.0.1"
sbx host $id 5173 --public
curl -s -o /dev/null -w "%{http_code}\n" https://frank-ready-marsh-5173.box.sandboxer.com/502The 502 page says "Nothing is answering on port 5173". Host it with --localhost and the sandbox relays the connection to the loopback address:
sbx host $id 5173 --public --localhost
curl -s https://frank-ready-marsh-5173.box.sandboxer.com/only on loopbackEach host call sets the mode again, so hosting without --localhost turns the relay off. Don't use --localhost for an app that binds 0.0.0.0. Some dev servers also reject unknown Host headers: for Vite, add .box.sandboxer.com to server.allowedHosts.
WebSockets#
We ran a small WebSocket echo server on port 8765 and connected with Node 24's built-in WebSocket. On a public port it works as is:
wss://frank-ready-marsh-8765.box.sandboxer.com/ -> echo: helloOn a private port, how you pass the token matters:
| Handshake carries | Result |
|---|---|
| Nothing | 401 |
?_token=<token> |
302 redirect, which WebSocket clients treat as a failed handshake |
The __sbx_auth cookie |
101 Switching Protocols, works |
In a browser this works without extra steps: opening the token link sets the cookie, and the page's own WebSocket connections send it. A script has to trade the token for the cookie first:
// node ws-cookie.mjs "<private hosted URL with ?_token=…>"
const url = new URL(process.argv[2]);
const res = await fetch(url, { redirect: "manual" });
const cookie = res.headers.get("set-cookie").split(";")[0];
url.searchParams.delete("_token");
url.protocol = "wss:";
const ws = new WebSocket(url, { headers: { Cookie: cookie } });
ws.onopen = () => ws.send("hello");
ws.onmessage = (e) => { console.log(e.data); process.exit(0); };echo: helloList and take down#
sbx host list $idPORT ACCESS URL TITLE
5173 public (localhost) https://frank-ready-marsh-5173.box.sandboxer.com
8000 private https://frank-ready-marsh-8000.box.sandboxer.com?_token=%E2%80%A6 DemoThe list hides tokens. sbx host hide $id 5173 removes the route and turns off the relay. The URL then returns 404 ("Port not hosted"), but the server keeps running inside the sandbox. A port you never hosted returns the same 404. Only hosted ports are reachable this way.
After stop, resume and fork#
- Stopped: while the sandbox was stopped, its URLs returned
404("No sandbox uses this address"). - Resumed:
sbx resumebrought back every route and token. In our test the resume restored memory, so the samehttp.serverprocesses (same PIDs) answered at once: the public URL returned200, the old private link returned its302, and a 30-day cookie from before the stop still worked. Memory is restored only when the resume lands on a machine with the same CPU type. Otherwise the sandbox boots from disk and showsmemoryRestored: false: routes and tokens are kept, but you must start your servers again (or run them as enabled systemd services). - Forked:
sbx forkgave a new subdomain (woody-merry-quarry), no hosted ports and no running servers. Start your server and host it again on the fork.
Sandboxer hosting or a tunnel?#
Tunnels like ngrok and Cloudflare Tunnel solve a related problem: they publish a port on a machine you already run, through an agent process that connects out. The table uses only what each vendor's own docs say.
sbx host |
ngrok | Cloudflare quick tunnel | Cloudflare named tunnel | |
|---|---|---|---|---|
| Command | sbx host <id> <port> |
ngrok http 8080 (quickstart) |
cloudflared tunnel --url http://localhost:8080 (docs) |
Configured in the dashboard or API (docs) |
| Extra process to run | None | The ngrok agent | cloudflared |
cloudflared |
| Account | Your Sandboxer account | An ngrok account and authtoken | Not needed | Cloudflare account, with a website added to Cloudflare |
| Hostname | <subdomain>-<port>.box.sandboxer.com, the same across stop and resume |
A dev domain chosen automatically for your account | A random trycloudflare.com name that changes on each start |
Your own hostname |
| Access control | Private token by default, or --public |
OAuth, basic auth and more through Traffic Policy (auth) | None listed | Open to anyone unless you add a Cloudflare Access application |
| Stated limits | Only hosted ports are reachable | 200 in-flight requests, no Server-Sent Events, no uptime guarantee, for testing |
Use sbx host when the server already runs in a Sandboxer sandbox. Use a tunnel when the server runs somewhere else, such as your laptop, or when you need your own domain today. Sandboxer doesn't offer first-class custom domains yet: you can point a CNAME at the hosted name and terminate TLS yourself, as described in Hosting.
FAQ#
Is the URL public by default?#
No. A new hosted port is private. Requests without the URL's _token (or the cookie it sets) get 401, and they never reach your server. Add --public for an open URL.
Why do I get 502 "Nothing is answering"?#
Either the server isn't running, or it listens on 127.0.0.1 only. Bind it to 0.0.0.0, or host the port with --localhost.
Do WebSockets and hot module reload work?#
Yes. On a public port they work directly. On a private port the handshake needs the __sbx_auth cookie, which a browser already has after opening the token link.
Does the URL change when the sandbox stops and resumes?#
No. Routes and tokens are kept, and the subdomain stays the same. A fork gets a new subdomain with no hosted ports.
Do I need to open the firewall for a hosted port?#
No. Hosted traffic comes through the HTTPS proxy. You only need sudo ufw allow <port> to serve directly on the sandbox's IPv6 address.
Related#
More guides
For running AI-written code we rank Sandboxer, Freestyle and E2B first: each gives code its own VM with an API or SDK and can pause with memory. Pick Daytona for Windows or GPUs, Modal or Northflank for GPUs at scale, E2B or Northflank to run in your own cloud, and Vercel Sandbox if your app lives on Vercel.
For coding agents we rank Sandboxer first: a full Ubuntu VM with Claude Code and Codex preinstalled, Docker, and stop-and-resume that keeps the agent's session in memory. Freestyle and Amp Orbs also keep memory across pauses. Pick E2B for prebuilt agent templates on open source, Docker Sandboxes for free local runs, and Codespaces if you live in GitHub.
The best Daytona alternatives depend on why you're switching. For a full Linux microVM on every sandbox with memory-saving stop and Docker, we rank Sandboxer first. E2B suits open source and self-hosting, Modal massive short-job fleets, Northflank bring-your-own-cloud with GPUs, and Vercel Sandbox apps on Vercel.
If you're leaving E2B for longer sessions or a fuller machine, we rank Sandboxer first, then Freestyle: both give each sandbox a full Linux VM that keeps memory across pauses, with Docker and no short session cap. Pick Daytona for Windows or GPUs, Northflank for bring-your-own-cloud, and stay on E2B if you need open source.