Preview a dev server running on a remote machine
Your agent ran pnpm dev on a remote machine, and the terminal says the app is at http://localhost:5173. That's the machine's localhost, not yours, so the link opens nothing on your laptop. There are two ways to bring it to your browser: a private SSH tunnel, or a public link.
Private: SSH port forwarding
SSH can carry a port from the remote machine to your computer. The -L option in ssh forwards a local port to a host and port on the remote side:
ssh -N -L 5173:localhost:5173 dev@203.0.113.10
Leave that running and open http://localhost:5173 on your laptop. -N tells ssh not to run a remote command, since you only want the forward. Add -f to send it to the background.
Three things make this the default choice:
- It's private. Unless you change
GatewayPorts, ssh binds the forwarded port to your loopback address (ssh_config), so other devices on your network can't open it. - The dev server needs no changes. Requests reach it as
localhost, the host it expects. - Nothing new is exposed. It rides on SSH access you already have.
The limit is that only you can see it, and only while the ssh command runs. To show a teammate or try the app on your phone, you need a link.
Public: a quick tunnel
Cloudflare's quick tunnels give a local port a temporary public address on trycloudflare.com, with no Cloudflare account or domain:
cloudflared tunnel --url http://localhost:5173
cloudflared prints an https:// address. The docs are direct about the trade-offs:
- Anyone with the URL can reach your service until you stop cloudflared.
- The hostname changes every time you start a tunnel.
- Each tunnel handles up to 200 requests in flight; more get a
429. - Server-Sent Events don't work through them.
- They're meant for testing and development, and have no uptime guarantee.
If you run cloudflared yourself and want to limit who gets in, quick tunnels can also ask visitors for a one-time PIN sent to an allowed email address, with --allowed-mail.
Why Vite says "Blocked request"
Open a Vite dev server through a tunnel and you'll probably see this instead of your app:
Blocked request. This host ("some-random-words.trycloudflare.com") is not allowed.
To allow this host, add "some-random-words.trycloudflare.com" to `server.allowedHosts` in vite.config.js.
Vite checks the Host header of every request. Its server options accept localhost, names under .localhost and IP addresses by default. Through a tunnel, the request arrives carrying the tunnel's hostname, which isn't on that list, so Vite refuses it. The check is there to stop DNS rebinding, a trick that lets a website you visit point its own domain at your dev server and read from it.
You have two ways past it.
Allow the tunnel's domain. In server.allowedHosts, a name that starts with a dot allows that domain and every subdomain under it:
// vite.config.js
export default {
server: { allowedHosts: ['.trycloudflare.com'] },
}
Allow only the domains you use. Setting allowedHosts to true turns the check off for every host, and Vite's docs warn against it for the DNS rebinding reason above. If you serve a production build with vite preview, the matching option is preview.allowedHosts.
Rewrite the Host header at the tunnel. cloudflared can set the Host header on the requests it sends to your local service. Cloudflare documents it as the httpHostHeader origin parameter, and the command line takes it as --http-host-header:
cloudflared tunnel --url http://localhost:5173 --http-host-header localhost
Vite now sees localhost and serves the page without any change to its config. The visitor's browser still shows the tunnel's address.
When a public link is fine
A preview link suits work you want someone to look at: a teammate reviewing a UI, a client checking a draft, your own phone on mobile data. It's the wrong tool when the app holds real data, has an admin page without sign-in, or talks to production services with real keys.
Keep in mind that a dev server sends your source code to the browser on purpose, so anyone with the link can read it. The random hostname makes a link hard to guess, but it stops being private once it's pasted into a chat. Stop the tunnel when the review is over, and use the SSH forward for anything that should stay private.
How shedix does it
Each shedix machine page has a Ports panel that lists the ports listening on the machine. Pick one and it makes a public preview link for that port. The link is a Cloudflare quick tunnel that sends Host: localhost to your dev server, so Vite accepts it with its default settings. Anyone with the link can open it until you stop it or the machine stops.
For private access, use SSH port forwarding. Every machine takes SSH with the keys on your profile, so the ssh -L command above works as written. The machine's IP address changes each time it starts, so check the current one before you connect.
Real Ubuntu covers what's on a machine, the security page explains what a preview link exposes, and the Ports calls are in the API docs.