# How Do I Run a Next.js Dev Server With a Live Preview URL?

> Run next dev on port 3000 in the sandbox and host the port with sbx host. The page loads right away, but hot reload stays broken until you add "*.box.sandboxer.com" to allowedDevOrigins in next.config.ts and restart. Then edits made in the sandbox show up in the browser in under a second.

Source: https://sandboxer.com/guides/run-a-next-js-dev-server-with-a-preview-url. Updated 2026-10-27.

**Short answer:** Run next dev on port 3000 in the sandbox and host the port with sbx host. The page loads right away, but hot reload stays broken until you add "*.box.sandboxer.com" to allowedDevOrigins in next.config.ts and restart. Then edits made in the sandbox show up in the browser in under a second.

This page is about the Next.js dev server only. For how hosted URLs, tokens and cookies work in general, see [Give a sandbox port an HTTPS URL](/guides/give-a-sandbox-port-an-https-url). For built previews of each pull request, see [Get a preview URL for every pull request](/guides/get-a-preview-url-for-every-pull-request).

## At a glance

| Symptom on the hosted URL | Cause | Fix |
| - | - | - |
| Page loads, but edits never show up until you reload | Next.js blocks its HMR WebSocket from origins it doesn't know | `allowedDevOrigins: ["*.box.sandboxer.com"]`, then restart `next dev` |
| Browser console: `WebSocket connection to 'wss://…/_next/hmr?id=…' failed` every few seconds | Same | Same |
| Server log: `⚠ Blocked cross-origin request to Next.js dev resource /_next/hmr` | Same | Same |
| `502 Nothing is answering on port 3000` | Dev server isn't running | Check the service and its log |
| URL works, then stops after the sandbox boots from disk | Dev server was a one-off process | Run it as an enabled systemd unit |

Everything below was run on a `small` sandbox (2 vCPU, 4 GB) on 2026-10-27 with Next.js 16.4.3 and Turbopack. We drove a real headless Chromium against the hosted URL to watch the HMR socket. Tokens are replaced with `<token>`.

## 1. Create the app

```bash
sbx new --name next-dev --type small
id=sb_t0uixywwik    # the id sbx new printed
sbx exec $id --timeout 600 "npx --yes create-next-app@latest web --yes"
```

```text
Success! Created web at /home/user/web
```

This took 37 seconds, including the npm install. The sandbox has Node 22 and npm preinstalled. `sbx exec` stops a command after 30 seconds by default, so pass `--timeout` for installs. The flags go before the command.

## 2. Allow the hosted origin

Next.js [blocks cross-origin requests to dev-only resources](https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins) by default. It allows `localhost` and the hostname it was started with. Your browser's origin is `https://<subdomain>-3000.box.sandboxer.com`, so that is blocked until you list it. Add one line to `next.config.ts`:

```typescript
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  allowedDevOrigins: ["*.box.sandboxer.com"],
  // …the rest of your config
};

export default nextConfig;
```

Next.js matches only the hostname, and `*` stands for exactly one label. Every hosted port's hostname has one label in front of `box.sandboxer.com` (`lucky-smart-magpie-3000`), so this single entry covers any sandbox and any port. It also covers forks, which get a new subdomain. If you'd rather allow just one URL, the server log prints the exact hostname to paste in (step 5). The config is read only at startup: restart `next dev` after you change it.

## 3. Run `next dev` as a service

`next dev` listens on `0.0.0.0` by default ([CLI docs](https://nextjs.org/docs/app/api-reference/cli/next)), so you don't need `--localhost` on the hosted port. Run it as a systemd unit, not a detached command, so it starts again if the sandbox ever boots from disk:

```bash
sbx exec $id "sudo tee /etc/systemd/system/next-dev.service >/dev/null <<UNIT
[Unit]
Description=Next.js dev server
After=network-online.target

[Service]
User=user
WorkingDirectory=/home/user/web
ExecStart=/usr/bin/npm run dev -- --port 3000
Restart=always
Environment=NEXT_TELEMETRY_DISABLED=1
StandardOutput=append:/home/user/next-dev.log
StandardError=append:/home/user/next-dev.log

[Install]
WantedBy=multi-user.target
UNIT
sudo systemctl daemon-reload && sudo systemctl enable --now next-dev && sleep 4 && cat next-dev.log"
```

```text
▲ Next.js 16.4.3 (Turbopack)
- Local:         http://localhost:3000
- Network:       http://10.202.0.82:3000
✓ Ready in 501ms
```

We send output to a file so `tail -f next-dev.log` works as your dev console. In sandboxes created on October 27, 2026, a plain `journalctl -u next-dev` showed no entries; `sudo journalctl -m -u next-dev` shows them.

## 4. Host port 3000

```bash
sbx host $id 3000 --title "Next dev"
```

```text
https://lucky-smart-magpie-3000.box.sandboxer.com?_token=<token>
```

The URL is private: without the token it answered `401`; with it, `302` and a cookie. The first page load after a (re)start compiled the route and took 3.0 seconds. The next load took 0.34 seconds.

Open the token link in your browser. The cookie it sets also covers the HMR WebSocket, so there is nothing extra to do for a private URL. Scripts that connect without a browser need to trade the token for the cookie first, as [shown here](/guides/give-a-sandbox-port-an-https-url).

## 5. What happens without `allowedDevOrigins`

We ran the same app first without step 2. The page rendered (`200`), so everything looked fine. But the browser's HMR socket never opened:

```text
10:23:24.927 ws open /_next/hmr
10:23:25.156 console error WebSocket connection to 'wss://lucky-smart-magpie-3000.box.sandboxer.com/_next/hmr?id=…' failed: Error during WebSocket …
10:23:25.156 ws close /_next/hmr
…              (the client retried every 1 to 5 seconds)
```

The server logged why:

```text
⚠ Blocked cross-origin request to Next.js dev resource /_next/hmr from "lucky-smart-magpie-3000.box.sandboxer.com".
To allow this host in development, add it to "allowedDevOrigins" in next.config.js and restart the dev server:
```

We then edited the heading in `app/page.tsx` in the sandbox. Next.js recompiled (`✓ Compiled in 218ms`), but the open page kept the old text for the remaining 20 seconds of the test. Only a manual reload would have shown the change. A dev-asset request that carries an `Origin` header also got `403`, while plain script loads got `200`. That is why the page renders but live updates fail.

## 6. Hot reload over the hosted URL

With `allowedDevOrigins` set and the server restarted, we loaded the page and then ran one edit from our laptop:

```bash
sbx exec $id "sed -i 's/To get started, edit the/Edit one, from the sandbox:/' web/app/page.tsx"
```

What the browser saw:

```text
10:24:14.361 console log [HMR] connected
10:24:14.362 ws frame turbopack-connected
10:24:14.805 loaded, h1 = "To get started, edit the page.tsx file."
10:24:25.288 (sbx exec returned)
10:24:25.357 ws frame serverComponentChanges
10:24:25.643 ws frame built
10:24:25.664 h1 changed to "Edit one, from the sandbox: page.tsx file."
```

The heading changed 0.38 seconds after the edit command returned, with no reload from us. This loop is what you want when a coding agent edits files in the sandbox and you watch the result in your browser.

## Stop, resume and memory

| Check | Result |
| - | - |
| `sbx stop $id --wait` | 2.2 s; the URL answered `404` while stopped |
| `sbx resume $id` | 1.3 s, `memoryRestored: true` |
| First request after resume, old cookie | `200` in 0.26 s: the same dev server, already warm |
| HMR after resume | `[HMR] connected`; the next edit showed up live |
| Memory on `small` | `next-server` used about 435 MB; the sandbox used 768 MB of 3,914 MB |

Routes, tokens and browser cookies survive a stop. If a resume can't restore memory (`memoryRestored: false`, for example on a different CPU type), the sandbox boots from disk. The enabled unit then starts `next dev` again and the first page load compiles once more. We did not hit that case in this test.

## FAQ

### Do I need `--localhost` when I host the port?

No. `next dev` binds `0.0.0.0` by default and logs a `Network:` address. Use `--localhost` only if you started it with `-H 127.0.0.1`.

### Can I put the full URL in `allowedDevOrigins`?

No. Next.js matches only the hostname, so leave out `https://`, the port and the `?_token=`. Never commit a token to `next.config.ts`.

### Does this affect `next build` and `next start`?

No. `allowedDevOrigins` applies only in development. A production server has no HMR socket. For built previews, see [Get a preview URL for every pull request](/guides/get-a-preview-url-for-every-pull-request).

### Why is the first page load slow?

`next dev` compiles each route on its first request. In our test that took 3.0 seconds on a `small` sandbox, and later loads took about 0.3 seconds. After a restart, or a resume that boots from disk, the first load compiles again.

### Can I share the preview with a teammate?

Yes. Send the private token link. It works in their browser, HMR included, because the cookie covers the WebSocket. Switching the port to `--public` and back mints a new token, which cuts off old links.

## Related

- [Give a sandbox port an HTTPS URL](/guides/give-a-sandbox-port-an-https-url)
- [Get a preview URL for every pull request](/guides/get-a-preview-url-for-every-pull-request)
- [Idle auto-pause and wake on use](/guides/idle-auto-pause-and-wake-on-use)
- [Hosting](/docs/hosting)
