Next.js Dev Server Through a Tunnel
Why Next.js needs extra config with tunnels
Next.js dev server includes security checks that block requests from unexpected origins. When you access your app through a tunnel URL like https://k7m2x9p4q1.localtolink.com, the browser sends that hostname as the request origin — not localhost.
Starting with Next.js 15.2, the dev server validates origins and may block cross-origin requests or show errors when loading through a tunnel. You need to explicitly allow your tunnel domain.
This guide covers tunnel setup, allowedDevOrigins configuration, HMR over WebSocket, and a production-build fallback.
Prerequisites
- Next.js 15.2 or later (for
allowedDevOrigins) - Node.js 22.14+ for LocalToLink
- Dev server running on port 3000 (default) or your configured port
Step 1: Start the tunnel
Terminal 1 — Next.js dev server:
npm run dev
Terminal 2 — LocalToLink:
npx localtolink --port 3000
Copy the public URL from the output, e.g. https://k7m2x9p4q1.localtolink.com.
Step 2: Configure allowedDevOrigins
Add your tunnel domain to next.config.js or next.config.ts:
/** @type {import('next').NextConfig} */
const nextConfig = {
allowedDevOrigins: ["*.localtolink.com"],
};
export default nextConfig;
The wildcard covers all random subdomains LocalToLink assigns. You do not need to update this config when the subdomain changes.
Restart npm run dev after saving the config file. Next.js reads config at startup.
Step 3: Open through the tunnel URL
Navigate to the tunnel URL in your browser (not localhost:3000). On first visit, continue through any LocalToLink safety prompt if shown.
Your app should load with assets, API routes, and Fast Refresh working.
What allowedDevOrigins fixes
Without this setting, you may see:
- Cross-origin errors in the browser console
- Blocked dev server requests when loading pages through the tunnel
_next/staticor HMR connection failures- "Invalid origin" messages from the Next.js dev server
The setting tells Next.js that requests from *.localtolink.com are trusted during development.
WebSocket / Fast Refresh (HMR)
Next.js Fast Refresh uses WebSockets for hot updates. LocalToLink supports WebSocket passthrough — no extra tunnel configuration needed.
Once allowedDevOrigins is set, HMR generally works through the tunnel. If hot reload fails:
- Check browser console for WebSocket errors
- Confirm you opened the app via the tunnel URL (not localhost)
- Try the production build fallback below
For mobile HMR testing, see Test Localhost on Your Phone.
Fallback: production build locally
If dev-server origin checks still cause issues, test through a production build. Production mode does not apply dev-only origin restrictions:
npm run build
npm run start
npx localtolink --port 3000
Trade-off: no Fast Refresh, but identical runtime behavior to deployed production.
Browser interstitial
When the visitor interstitial policy is enabled, browser access through the tunnel may show LocalToLink's one-time Opening a shared link page. Continue once per subdomain. Webhook and JSON API requests typically skip the page; only human browser navigations need the continue flow (or the localtolink-skip-browser-warning header in tests).
Environment variables and tunnel URLs
If your app uses NEXT_PUBLIC_* variables with hardcoded localhost URLs, update them for tunnel testing:
# .env.local — use relative paths when possible
NEXT_PUBLIC_API_URL=/api
Relative URLs work regardless of whether you access via localhost or tunnel.
Session notes
| Limit | Impact |
|---|---|
| 2-hour session | Restart tunnel for long dev sessions |
| Random subdomain | *.localtolink.com wildcard covers all subdomains |
| Custom subdomains | Not available on the free tier yet |
| 1 tunnel per IP | Stop other tunnels before starting |
Troubleshooting
| Issue | Fix |
|---|---|
| Cross-origin blocked | Add allowedDevOrigins; restart dev server |
| HMR not connecting | Open app via tunnel URL; check WebSocket in DevTools |
| Assets 404 | Confirm tunnel points to correct port (--port 3000) |
| Works on localhost, not tunnel | Expected without allowedDevOrigins — add config |
| Interstitial on every navigation | New subdomain — click through once; cookie lasts 24h |
| API routes timeout | Check handler logs; tunnel session may have expired |
Testing OAuth through Next.js
Register the tunnel URL as your OAuth callback:
https://SUBDOMAIN.localtolink.com/api/auth/callback
See OAuth Callback on Localhost for provider-specific steps.
Middleware and tunnel origins
If you use Next.js middleware that checks request.nextUrl.hostname, ensure it accepts tunnel hostnames during development:
const host = request.nextUrl.hostname;
const isDev = process.env.NODE_ENV === "development";
const isTunnel = host.endsWith(".localtolink.com");
// Allow tunnel origins in dev
Hardcoded localhost checks in middleware can block requests when accessed through the tunnel URL.
FAQ
Does this work with Next.js App Router and Pages Router?
Yes. allowedDevOrigins applies to the dev server regardless of routing model.
Do I need this for Vercel preview deployments?
No. This is for local development through a tunnel only.
What about Next.js 14 and earlier?
Origin checks were less strict. Upgrade to 15.2+ or use the production build fallback.
See also Vite HMR Through a Tunnel for Vite-specific configuration and Expose Localhost to the Internet for tunnel basics.
Try it now
Start a tunnel from your project directory — no account required.
$ npx --yes localtolink@latest