FeaturesGuidesBlogDocumentationPricingGet Started
Troubleshooting

Tunnel Not Working? Troubleshooting Guide

503 errors, changed URLs, broken WebSockets — diagnose localhost tunnel problems without guessing.

Start here

Before diving into tool-specific issues, confirm the basics:

  1. Your app is running on the port you expect
  2. The tunnel is running in a separate terminal
  3. You are using the HTTPS URL from current tunnel output — not yesterday's URL
  4. The path exists/webhook vs /webhooks matters
curl -I https://YOUR-SUB.localtolink.com/

You should get a response from your app (200, 302, 404 from your app — not a tunnel error page).

Common errors

URL changed after restart

Every new tunnel session gets a random subdomain on the free tier. Update webhook URLs, OAuth redirect URIs, and shared links.

LocalToLink grace period: brief disconnects within 10 minutes may reconnect to the same URL. Full restart = new URL.

503 Tunnel Unavailable

Usually means the tunnel process is not running or the session expired (2-hour limit on free tier). Restart:

npx localtolink --port 3000

If you were using a shared public tunnel service elsewhere, 503 often indicates overloaded shared infrastructure — switch to a dedicated service for reliability-critical tests.

502 Bad Gateway

Your app is not reachable on the configured port:

WebSocket / HMR broken

HTTP tunnels do not automatically proxy WebSockets correctly in all setups. For Vite:

// vite.config.js
export default {
  server: {
    hmr: { clientPort: 443, protocol: "wss" },
  },
};

See Vite HMR through a tunnel and WebSocket guide.

Webhook URL changed every restart

External services cache nothing about your old subdomain. When you restart npx localtolink, you get a new random subdomain on the free tier. Every registered webhook, OAuth redirect, and shared client link must be updated.

Workflow that minimizes pain:

  1. Start app + tunnel at the beginning of a dev session
  2. Register URLs once in provider dashboards
  3. Use provider "resend" / "replay" features instead of restarting the tunnel unnecessarily
  4. Keep a sticky note or env var with the current tunnel URL during active work

LocalToLink offers a 10-minute reconnect grace — brief network blips may restore the same URL without a new subdomain. A full process restart always assigns a new subdomain.

Corporate Wi-Fi blocks tunnel

Some corporate networks block non-standard domains. Try mobile hotspot to confirm. Not fixable from the tunnel side.

Decision table

SymptomFirst check
Nothing loadsApp running? Tunnel running?
Worked, then stoppedSession expired? Terminal closed?
Webhook 404Handler path correct?
WS disconnectsHMR config for your framework?
Slow responsesNormal tunnel latency; not localhost speed

Switching tools mid-debug

Your app does not care which tunnel forwards traffic:

  1. Stop current tunnel
  2. Start npx localtolink --port PORT
  3. Update external URLs
  4. Retry

No application code changes.

Systematic diagnosis order

When something fails, walk this list in order — skipping steps causes false leads:

  1. Local first: curl http://localhost:PORT/ — if this fails, fix your app before blaming the tunnel
  2. Tunnel process: terminal shows "Tunnel active" — if not, restart npx localtolink
  3. Public URL: copy from current terminal output, not chat history from yesterday
  4. Path: webhook at /api/webhooks/stripe is not the same as /webhook
  5. External config: Stripe/GitHub/OAuth dashboard matches step 3 exactly
  6. Session limits: 2-hour expiry, 1 GB bandwidth, 10 req/s — sustained load tests hit limits

Most reports of "tunnel broken" resolve at steps 1–3.

Docker-specific 502 diagnosis

Container networking causes more 502 errors than tunnel infrastructure:

# App in Docker, tunnel on host
docker run -p 3000:3000 my-app
npx localtolink --port 3000   # targets host port 3000

If the app binds only inside the container network without publishing -p, the host port has nothing listening. docker ps should show 0.0.0.0:3000->3000/tcp. For docker-compose, the tunnel --port must match the host side of the ports mapping.

Honest limits

LocalToLink enforces 2-hour sessions, 1 GB bandwidth, 10 req/s, 1 tunnel per IP. Hitting limits looks like failed requests or session termination — not random 503s from shared servers.

FAQ

Is the tunnel down globally?
Check your app locally first. Most "tunnel down" reports are expired sessions or stopped processes.

Can I get a fixed URL?
Not on free tier. Random subdomain each session.

Why does localtunnel behave differently?
Shared infrastructure tools have different failure modes. See our tunnel troubleshooting guide for generic advice.

How do I test if the tunnel reaches my app?
curl -v https://YOUR-SUB.localtolink.com/ from any machine. Compare response headers with curl http://localhost:PORT/.

Can I run two tunnels at once?
Free tier allows one tunnel per IP. Use different paths on one app or separate ports sequentially.

Latency expectations

Tunnel traffic adds one network hop between the client and your laptop. Typical added latency is tens to low hundreds of milliseconds — noticeable in demos but irrelevant for webhook delivery or OAuth redirects.

If responses feel sluggish:

Next steps

Try it now

Start a tunnel from your project directory — no account required.

$ npx localtolink

See the full CLI reference for flags and exit codes.

LocalToLink CLI 1.0.1 — verified 2026-09-05