
Keep remote Vite previews connected from the first page request to the HMR socket
A Vite application can load through a public HTTPS address while Hot Module Replacement still fails. The reason is usually that the page request and the HMR WebSocket follow related but separately negotiated connection paths. Docker networking, browser-visible ports, Vite host checks, and OAuth redirect validation can add further mismatches. In this guide, we explain how to expose a reachable Vite development server with a Localtonet HTTP tunnel and troubleshoot each layer without publishing credentials or weakening development safeguards.
Why the page can load while Vite HMR remains disconnected
A remote Vite preview involves more than one browser connection. The initial document, JavaScript modules, stylesheets, and development assets are ordinary HTTP requests. Hot Module Replacement uses a persistent WebSocket connection so the Vite development server can notify the browser when a source file changes. A successful page load therefore proves that the normal HTTP path works, but it does not prove that the HMR WebSocket completed its upgrade or reached the correct server.
With Localtonet, the client application on the development device establishes an outbound connection to one of our relay servers. An HTTP tunnel provides a public URL and forwards traffic toward the local IP address and port selected for the tunnel. This avoids inbound router port forwarding, a public IP requirement, VPN setup, or inbound firewall changes. The tunnel is available only while the selected device is connected and the tunnel is running.
For a typical remote preview, the request path is browser to public HTTPS endpoint, then relay to the Localtonet client, and finally client to the Vite target. The browser sees the public origin, while Vite may be listening on a private address such as a loopback interface, a LAN interface, or a container interface. HMR works only when the browser-generated WebSocket destination and the server-side target ultimately describe a compatible path.
If the application shell loads but edits do not appear, treat this as an HMR transport problem rather than proof that the entire tunnel is broken. Inspect the WebSocket connection independently from document and asset requests.
The connection layers that must agree

Understand the addresses before changing configuration

The word localhost is contextual. A browser on a colleague’s laptop interprets localhost as that laptop. A Vite process inside Docker interprets localhost as its own container. The Localtonet client running on the Docker host interprets localhost as the host. These are separate network namespaces even when they are part of one development workflow.
If Vite runs directly on the same machine as our client, the tunnel can target the local address and port on which Vite is reachable. If Vite runs inside Docker, the container’s service normally needs to be reachable from the environment where the Localtonet client runs. That may require an appropriate container listening address and a host-published port. Exact Docker settings depend on the project’s network mode and compose configuration, so verify reachability from the client device instead of assuming that a container port is automatically available on the host.
| Address perspective | What localhost means | Common failure | Diagnostic question |
|---|---|---|---|
| Remote browser | The viewer’s own computer | HMR attempts to connect to a port on the viewer’s device | Does the WebSocket URL use the public hostname? |
| Docker container | The running container | Vite listens only inside the container namespace | Can the Docker host reach the published Vite port? |
| Development host | The host operating system | The selected target port is not published or listening | Can the Localtonet client device reach the target directly? |
| Public tunnel | Not applicable | The browser uses a different scheme, host, or port for HMR | Does the socket destination match the public origin? |
Create the Localtonet HTTP tunnel
Start with a working Vite service that is reachable from the device running our client. Do not begin by changing HMR options. First establish that the local target is correct, then create and start the tunnel, and only then diagnose the browser-side socket.
Install and run the Localtonet client
Run our client on the device that can reach the Vite development server. If Vite is in Docker, confirm that this device can access the container through the intended host-published address and port.
Authenticate or select the client device
Use the device-specific authentication token through the supported product workflow. Never place the token in source control, browser code, screenshots, logs, or an OAuth configuration.
Select an available relay server
Choose a currently available server or region in the dashboard. Available server values can change and may vary by plan, so they should be selected from the current product rather than copied from an old guide.
Create an HTTP tunnel to the Vite target
Configure the HTTP tunnel with the local IP address and port that expose Vite to the Localtonet client device. HTTP process types can provide a random subdomain, a supported selected subdomain, or a custom domain, with the same content served at a public HTTPS address.
Start the tunnel
Creating a tunnel does not start it. Use the Start button and wait until the selected client is connected and the tunnel is running, then open the assigned public URL.
Stop or delete the tunnel after testing
When the remote preview or OAuth test is complete, stop the tunnel or delete it if it is no longer required. The public endpoint is usable only while the client remains connected and the tunnel is running.
Consult the Localtonet HTTP tunnel documentation during setup because current dashboard fields, server availability, and domain requirements can change. Do not hardcode a relay server value or assume that every process type is included in every plan.
Treat a public development preview as temporary exposure. Do not include secrets in client-side environment variables, commit device tokens, disable security checks globally, or leave the tunnel running without a reason. Use application authentication, least privilege, IP restrictions, and other suitable access controls where available and appropriate.
A systematic Vite HMR troubleshooting workflow

Testing one layer at a time identifies where the WebSocket path stops.
1. Confirm the ordinary HTTP path
Open the public HTTPS URL in a clean browser session. Check that the document and module requests return the expected application rather than an unrelated service, stale proxy response, or container default page. If the page does not load, focus on the local IP, local port, container publication, Vite listening interface, tunnel state, and device connectivity before investigating HMR.
2. Inspect the browser’s WebSocket request
Use the browser developer tools and look for the HMR WebSocket. Record the attempted scheme, hostname, port, path, and status. A destination such as a remote viewer’s localhost, an internal Docker hostname, or a private LAN address is incorrect for a browser outside that network. A socket that uses ws from an HTTPS page may be blocked as mixed content, while the corresponding secure browser-side form is normally wss.
3. Compare the socket destination with the public URL
The public origin is the browser’s frame of reference. If the preview opens at a public HTTPS hostname without an explicit port, but the HMR client tries to connect directly to Vite’s internal development port, the browser is attempting a different route. The client-facing HMR scheme, host, and effective port must represent the externally reachable endpoint, not merely the port used behind the tunnel.
Vite’s HMR configuration and fallback behavior vary by release. Options involving the HMR protocol, host, client port, server port, or path must be checked against the documentation for the exact Vite version in the project before publication or adoption. We intentionally do not provide a universal configuration snippet because a setting valid for one Vite release or topology may be ineffective or unsafe in another.
4. Verify Docker reachability from the client environment
Test the Vite target from the same host or network context in which the Localtonet client runs. A successful request inside the container does not prove host reachability. Likewise, a port declared inside a container image is not necessarily published to the host. Confirm that Vite listens on an interface suitable for the intended container network and that the selected host port reaches the correct container service.
5. Check host and origin handling without broad exceptions
Vite may validate the incoming host or apply origin-sensitive behavior. The request reaching Vite can contain information associated with the public preview, while Vite itself listens on a private target. If the server rejects the request, use the narrowest supported configuration for the exact public development hostname. Avoid permissive settings that accept arbitrary hosts, especially while a development server is publicly reachable.
6. Look for reconnect loops and fallback behavior
When the HMR socket fails, the browser may repeatedly reconnect, reload, or fall back according to the behavior of the installed Vite version. Repeated attempts are a symptom, not a diagnosis. Identify the first failing socket request and determine whether it was blocked by the browser, rejected by Vite, directed to the wrong endpoint, or unable to reach the target.
Common symptoms and the most useful interpretation
| Symptom | Likely layer | What to inspect first |
|---|---|---|
| The public page does not load | Base HTTP path | Tunnel state, device connection, target address, target port, and Docker publication |
| The page loads but HMR stays disconnected | WebSocket path | Socket scheme, hostname, port, path, and upgrade response |
| The browser tries localhost | Client-facing HMR destination | Version-specific Vite HMR host and port behavior |
| The browser reports mixed content | Scheme mismatch | HTTPS page combined with an insecure browser-side WebSocket URL |
| Vite reports an unaccepted host | Development server validation | Narrow allowlisting for the exact public development hostname |
| It works on the host but not through Docker | Container networking | Listening interface, published port, and client-to-container reachability |
| OAuth returns a redirect mismatch | Provider callback validation | Exact public scheme, hostname, effective port, and callback path |
Using the public URL for OAuth callback testing

OAuth introduces another origin boundary. The authorization provider redirects the browser to a URI registered for the application. For remote testing, that URI should use the public tunnel origin and the application’s exact callback path. A callback configured for local HTTP is not equivalent to the public HTTPS callback, and a generated hostname from a previous tunnel is not interchangeable with the current one.
Use one public origin consistently in the browser, OAuth provider configuration, application redirect construction, and any development allowlist. If a generated public hostname changes, update the registered redirect URI before testing again. Custom-domain DNS instructions and exact domain setup requirements should always be checked against current Localtonet documentation rather than inferred.
Do not place client secrets, authorization codes, access tokens, refresh tokens, Localtonet device tokens, or private callback data in frontend source code, public repositories, screenshots, or shared diagnostic output. Redact query parameters and headers before sending logs to another person.
HMR and OAuth can fail independently. A successful OAuth redirect proves that the browser can reach the callback route, but it does not prove that the HMR WebSocket is connected. Conversely, working HMR does not prove that the OAuth provider has an exact redirect URI match. Diagnose the callback as an HTTP navigation and the HMR channel as a WebSocket connection.
Frequently asked questions
Why does my Vite page load through HTTPS while HMR fails?
The page and module files use ordinary HTTP requests, while HMR depends on a separately negotiated WebSocket. The base tunnel path can be correct even when the socket uses the wrong hostname, scheme, port, or path, or when its upgrade is rejected.
Should a remote Vite browser connect to localhost for HMR?
No. In the remote browser, localhost refers to the viewer’s own device. The browser-facing HMR destination must represent the externally reachable development endpoint, while the Localtonet tunnel separately targets the private Vite address reachable by our client.
Does Vite need to serve HTTPS locally?
Not necessarily. The browser can use a public HTTPS origin while the tunnel targets a locally reachable HTTP service. However, the exact HMR behavior depends on the installed Vite version and topology, so confirm current Vite documentation before changing protocol-related settings.
Why can the Localtonet client not reach Vite inside Docker?
Vite may be listening only inside the container, or its port may not be published to the host where our client runs. Confirm the listening interface, container port publication, host address, and direct reachability from the Localtonet client device.
Can I disable Vite host checks to make the public preview work?
Broadly disabling host validation is not the recommended troubleshooting shortcut. Use the narrowest version-supported configuration that accepts the intended development hostname, and keep the preview temporary and appropriately protected.
Which URL should I register as an OAuth redirect URI?
Register the exact public HTTPS origin and callback path used by the development application. Match the scheme, hostname, effective port, and path precisely. If the public hostname changes, update the provider configuration before testing again.
Does creating a Localtonet tunnel make it immediately available?
No. After creating the tunnel, you must start it. It remains available only while the selected client device is connected and the tunnel is running. Stop or delete it when the remote development session is finished.
Share your Vite preview with Localtonet
Run our client on the device that can reach Vite, create an HTTP tunnel for the development target, and troubleshoot the page and HMR socket as separate connection paths. Keep the endpoint temporary, protect the application, and stop the tunnel when testing is complete.
Get Started Free →