Skip to main content
← All guides

Fix hot reload through a tunnel

Checked against the qURL™ CLI 3.4.1 source and documentation on October 9, 2026. Link and session behaviour is tested separately.

Content updated October 18, 2026. The verification date above records the original review; later behaviour checks are dated in the guide.

Why doesn’t hot reload work when I open my dev server through a tunnel?

Hot reload through a tunnel fails for one of three reasons. The dev server rejects the tunnel’s hostname, which Vite’s server.allowedHosts or Next.js allowedDevOrigins fixes; the hot reload WebSocket does not get through; or a streamed response needs Server-Sent Events, which Cloudflare Quick Tunnels do not support. In our test, qURL carried both the WebSocket and Server-Sent Events to a browser opening the qURL link.

What you need

  • A Vite or Next.js dev server opened through a tunnel
  • The browser console open on the page that doesn’t update

Steps

  1. Tell the three failures apart

    A host rejection stops the page itself: Vite shows “Blocked request. This host is not allowed”, and next dev logs “Blocked cross-origin request to Next.js dev resource”. A WebSocket failure lets the page load, but edits never arrive, and the console shows the hot reload socket failing to connect. A Server-Sent Events problem leaves the page and hot reload working while a streamed response, such as a chat reply, stalls; in the Network tab its event-stream request never delivers events. Each has a different fix below.

  2. Host rejected: let the dev server accept the tunnel’s hostname

    Vite answers “Blocked request” for an unknown host, and Next.js 16.2 and later block cross-origin requests to dev resources, including the hot reload socket. Add .qurl.site (or your tunnel’s domain) to Vite’s server.allowedHosts, or *.qurl.site to Next.js allowedDevOrigins, and restart.

  3. WebSocket failing: check the console for Vite’s fallback message

    If the WebSocket through the tunnel fails, Vite’s client falls back to connecting straight to the dev server, bypassing the tunnel, and logs “Direct websocket connection fallback.” That fallback only works on the machine running Vite, so a remote viewer gets no updates.

  4. Use a tunnel that carries WebSockets, or point Vite at it

    Vite’s docs list three fixes: have the proxy carry WebSockets; or set server.strictPort to true and server.ws.clientPort to the same value as server.port; or give server.ws.port a different value from server.port. Vite 8.1 renamed server.hmr to server.ws and still syncs the old names. Through qURL no change is needed: in our test the hot reload socket connected and edits reached the browser opening the qURL link.

    qurl publish http://127.0.0.1:5173
    qurl share <CRID>
  5. Stream stalling: check the tunnel carries Server-Sent Events

    This is not a hot reload problem: Vite’s hot reload uses a WebSocket, while chat replies, progress feeds and some dev tools stream over Server-Sent Events. Cloudflare Quick Tunnels do not support Server-Sent Events, so a stream that stalls there needs a different tool. In our tests with qurl 3.2.0 on October 1 and qurl 3.5.0 on October 18, six events sent a second apart arrived one by one through a qURL link.

What the recipient sees

With the socket through, an edit to the source appears in the browser opening the qURL link without a manual refresh. Through a qURL link it did with Vite 6 (October 1), and with Vite 8.3.4 and Next.js 16.4.2 (October 18), each once its hostname setting was in place.

Which tools carry hot reload to a remote browser?

OptionChoose it whenTrade-off
qURLqURL fits when a remote viewer needs live updates: in our test, Vite’s hot reload WebSocket and Server-Sent Events both passed through a qURL link.qURL still needs .qurl.site in Vite’s server.allowedHosts, or *.qurl.site in Next.js allowedDevOrigins; in our October 18 test both then hot-reloaded through a qURL link.
ngrokngrok fits when you already use it: ngrok’s docs say WebSocket connections work with no configuration.ngrok needs .ngrok-free.app (or your domain) in the dev server’s allowed hosts, an account, and a public URL.
Cloudflare Quick TunnelsA Cloudflare Quick Tunnel fits hot reload over a WebSocket with no account: Cloudflare’s Tunnel FAQ says Cloudflare Tunnel fully supports WebSockets.Cloudflare Quick Tunnels do not support Server-Sent Events, so an app or dev tool that streams over SSE stalls. Cloudflare documents that limit for Quick Tunnels, not for named tunnels.

Troubleshooting

If you seeDo this
Updates work on your machine but not for the person you shared withThe Vite client fell back to a direct connection that only your machine can make. Check the console for the fallback message and use a tunnel that carries WebSockets.
A tool that streams with Server-Sent Events stalls on a Cloudflare quick tunnelQuick Tunnels do not support Server-Sent Events, which is a separate limit from WebSockets. Use another tool; in our test, qURL passed Server-Sent Events through unbuffered.
webpack-dev-server shows “Invalid Host header”Its host check rejected the tunnel. Add the domain to devServer.allowedHosts; a leading dot covers subdomains. In Create React App, WDS_SOCKET_HOST and WDS_SOCKET_PORT set where the hot reload socket connects. Avoid allowedHosts: ‘all’ and DANGEROUSLY_DISABLE_HOST_CHECK: both turn the check off, which webpack and Create React App warn against.

Frequently Asked Questions

Does qURL support WebSockets and hot reload?

Yes. In our test through a qURL link, WebSocket upgrades reached the local app, Server-Sent Events and streamed responses arrived unbuffered, and Vite’s hot reload kept working once server.allowedHosts included .qurl.site.

Do Cloudflare quick tunnels support WebSockets?

Cloudflare’s Tunnel FAQ says Cloudflare Tunnel has full support for WebSockets; its quick tunnel page does not mention them either way. It does say quick tunnels do not support Server-Sent Events and allow 200 in-flight requests.

Should I set server.hmr or server.ws?

Either on Vite 8.1 and later, which renamed the WebSocket options to server.ws and keeps the old server.hmr names in sync. On earlier versions, use server.hmr.

Who else can reach my app while the link is open?

Opening the link admits the network address it was opened from for the session, which lasts 1 hour by default. Other devices that share that public IP address, such as people on the same office or café network or VPN, can reach the app until the session ends. By default a link admits whoever opens it; access rules set through the API, such as IP allow and deny lists, can restrict that. Keep your app’s own sign-in for anything sensitive.

Publish your app now

Install the CLI, then run one command. No sign-up, no API key.

Install the CLI ↗

Contact sales

Contact our team for enterprise deployments.

Enterprise starts with a scoped, paid pilot on one application, typically 60 to 90 days, credited in full toward year one if you convert within 60 days. Then an annual package sized to what you protect. Not per seat.