You started a tunnel, pointed a webhook at it, and got an ERR_NGROK_* page
instead of your handler. Here's what each of the common ones means, the fastest fix, and —
because most of these only exist because a tunnel is in the loop — how to keep
webhook traffic out of the tunnel in the first place.
Your plan caps concurrent agent sessions — the classic message is
"limited to 1 simultaneous ngrok agent session" (older free accounts), and the current free
plan allows 3. Start one more ngrok http … than the cap — in another terminal,
another machine, a leftover process, a Docker container you forgot — and it's rejected.
Fix: find and stop the other agent (check the ngrok dashboard's Agents
page, or pkill ngrok locally). Need two ports? One agent can serve multiple
tunnels: define both in ngrok.yml under tunnels: and run
ngrok start --all.
The URL exists but nothing is behind it: the agent that created it isn't running, crashed, or your laptop went to sleep — or you're using an old URL. Random/ephemeral endpoint URLs change on every run, so yesterday's URL in a webhook provider's settings is a guaranteed 3200. (The free plan's assigned dev domain is stable — but it still serves 3200 the moment its agent is offline.)
Fix: restart the agent; if the URL changed, update it everywhere it's configured. (If this is the third time this week, see the last section — this error class is the strongest argument for a stable URL that isn't tied to a running process.)
ngrok enforces a minimum agent version per account; older binaries are
refused. You hit this most often with a bundled or wrapped ngrok — an old Colab/Jupyter
recipe, pyngrok pinning an ancient binary, a dev-tool bundle that ships its
own copy.
Fix: ngrok update, or download the current binary and make
sure the thing you're running actually uses it (wrappers often cache their own copy —
upgrade the wrapper too).
Free endpoints serve an interstitial "you are about to visit" page to anything that looks like a browser. Humans can click through; automated checks that fetch your URL with a browser-ish User-Agent get the splash instead of your app.
Fix: send the header ngrok-skip-browser-warning: 1 (any
value works) or a custom non-browser User-Agent with the request. If you can't control the
caller's headers, the interstitial stays.
108, 3200, and the URL-rotation dance all come from one design fact: a tunnel URL is alive only while a process runs on your machine. Webhooks don't need that. Flip the model: give the provider a stable capture URL that's always up, and pull deliveries down to localhost yourself:
$ curl https://hookden.pages.dev/new $ curl -s https://hookden.pages.dev/cli -o hookden && chmod +x hookden $ ./hookden relay YOUR_BIN http://localhost:3000/webhook
The capture URL never rotates and never goes offline — deliveries land there even while your laptop is closed, and the relay re-delivers them (byte-exact body, so HMAC signatures still verify) when you're back. No agent sessions to collide, no interstitial, no version police. How the relay works →
Honest scope: this replaces a tunnel for webhook/callback traffic. If you need to demo a whole interactive app to a human, you still want a real tunnel — comparison of the free options here.
No signup needed. Or from your terminal: curl https://hookden.pages.dev/new