D
P
0

Next.js

Next.js 16 Works on `localhost` but HMR Dies Over a LAN IP? Add `allowedDevOrigins`

July 8, 2026·4 min read
Next.js 16 Works on `localhost` but HMR Dies Over a LAN IP? Add `allowedDevOrigins`

I was building a client dashboard in Next.js 16, and as usual, once the layout started settling I wanted to see it on a real phone. Not the responsive mode in DevTools, but an actual device on the same Wi-Fi. So I opened my machine's LAN IP from my phone, something like http://192.168.1.9:3000, and the page rendered perfectly. At that point everything looked fine.

The problem only showed up once I started editing. On the laptop, I changed a string in the hero component, hit save, and the desktop browser refreshed as it always does. But my phone just sat there. No change, no flash, nothing. I saved again, swapped a color to make it obvious, still nothing. The page on the phone was like a frozen screenshot: rendering correctly, but completely dead to changes.

My first instinct, as usual, blamed the wrong thing: maybe the Wi-Fi was flaky, or the phone was caching the page too aggressively. I hard-refreshed, I even disabled the cache in the phone's Safari. Still frozen. It was only after I opened the console on the phone that the puzzle started to show itself.

Finding the real trail

The phone's console was full of blocked cross-origin errors. Requests to _next/webpack-hmr, the internal endpoint Next.js uses to push hot module reload updates to the browser, were being refused. Not a failed connection, not a timeout. Explicitly blocked for origin reasons.

That immediately changed how I saw the problem. The main page loaded, so the server was clearly answering requests from the LAN IP. But the HMR connection, which runs separately and persistently in the background, was refused. And the crucial detail: on localhost:3000 everything worked perfectly. Only the network IP misbehaved. That is a very specific pattern, and once I saw it, I stopped blaming the Wi-Fi and started suspecting configuration.

Why this happens

Next.js 16 added a default cross-origin protection for the dev server. Requests to internal dev endpoints like _next/webpack-hmr coming from an origin other than localhost are blocked unless that origin is explicitly allowlisted. The idea makes sense from a security angle: a dev server shouldn't accept HMR connections from arbitrary origins.

The catch is that when I open the app via the network IP, that counts as a different origin. http://192.168.1.9:3000 is not localhost. So the HTML page is allowed to load, but the HMR connection coming from that IP origin is refused outright. Not a bug, not the Wi-Fi, not the cache. This is deliberate default behavior, and I just tripped over it because the way I test, from a phone over the IP, is exactly an origin the dev server didn't recognize yet.

Once that clicked, it all made sense. localhost works because it is an implicitly trusted origin. The LAN IP doesn't work because it is a foreign origin I never allowlisted. Nothing was broken. The only broken thing was my assumption that if the page loaded, the dev server was accepting every connection from that origin.

The fix

The fix is not to downgrade Next.js or turn off the protection. The fix is to tell the dev server that the network origin is one I trust. Next.js exposes an allowedDevOrigins option in the config file for exactly this.

I added the network origin to next.config:

// next.config.js
const nextConfig = {
  allowedDevOrigins: ['192.168.1.9', 'http://192.168.1.9:3000'],
}
 
module.exports = nextConfig

Restart the dev server, reopen from the phone over the same IP, and now HMR works exactly like it does on the desktop. Save on the laptop, the phone refreshes right along with it. Requests to _next/webpack-hmr are no longer blocked because the origin is on the allowlist.

I included both the bare hostname and the full URL with the port because, depending on the setup, one form can match the origin the browser actually sends more reliably. Adding both closes the gap without me having to guess which format Next.js uses to match.

If your machine's IP moves around, say DHCP hands out a different address each day, you'll need to update this list when it does. For a more stable workflow, I usually pin the machine's local IP with a DHCP reservation on the router, so the origin in allowedDevOrigins stays valid day to day.

The takeaway

If a Next.js 16 app works perfectly on localhost but hot reload goes silent when opened over a LAN IP, don't jump straight to blaming the Wi-Fi or the phone's cache. Open the console on that device first, and check for requests to _next/webpack-hmr being blocked cross-origin. If they are, that's Next.js 16's default cross-origin protection doing its job, and the fix is a single line: add the network origin to allowedDevOrigins in next.config. Treat the "works-on-localhost-not-over-network" gap as a real config bug, not just a dev-env quirk. Since then, my first move when the phone goes silent isn't a hard-refresh but a question: which origin am I on, and does the dev server actually recognize it?