All guides
Dev Server

Put a local dev server or API online

Give the app running on your own laptop a public HTTPS address, the way a tunnelling tool like ngrok does — for webhooks that have to call you back, for showing work in progress, and for testing on a real phone. The address belongs to your account, so it is the same every time.

What this does

A mapping does not have to point at a router. Install the Windows, Mac or Linux agent on the machine you develop on, point a mapping at 127.0.0.1 and the port your framework prints on startup, and the app you are building answers on a public HTTPS address from anywhere.

Nothing about your code has to change. The dev server keeps listening on localhost, keeps hot-reloading, and keeps its breakpoints — requests simply arrive from the internet as well as from your browser.

The address is part of your MikRouter account, so it is the same every time— it does not change when the dev server restarts, when your laptop reboots, or when you come back tomorrow. You can paste it into a payment gateway once and leave it there.

Before You Start

  • A MikRouter account with an agent created under Agents — keep its tunnel host and token nearby.
  • The agent installed and connected on your development machine Windows, Mac or Linux. Its card under Agents should read Online before you go on.
  • A dev server you can start, and the port it listens on — 3000, 5173, 8000, whatever it prints.
  • Not an ESP32.A board cannot run your code, and it holds only a handful of connections at once — one modern web page uses more than that on its own. Use a computer agent for this.

How the request gets to you

Anyone, anywhere

https://my-api.test.mikrouter.com

A browser, a phone on mobile data, or a payment gateway's servers.

MikRouter agent server

Provides the certificate

Then hands the request down the tunnel your agent already dialled out and is holding open.

Your machine

127.0.0.1:3000

The agent connects to your dev server exactly as your own browser does.

Nothing is listening on the internet and no port is forwarded. Your machine dialled out, so this works behind CGNAT, on hotel Wi-Fi, and on a network you do not control.

1

Start your dev server and note the port

Run it the way you always do. Leave it on localhost— you do not need 0.0.0.0 or any special flag while the agent is on the same machine.

Your development machine
$ npm run dev   ▲ Next.js 16.0.0  - Local:        http://localhost:3000   ✓ Ready in 1.4s   POST /api/webhooks/stripe 200 in 38ms  host: my-api.test.mikrouter.com

Here the port is 3000. Vite prints 5173, Django 8000, Rails 3000. Whatever it says is what goes into the mapping.

2

Open your agent and add a Web mapping

Go to Agents, open the agent running on this machine, and add a mapping under Mappings.

FieldValue
KindWeb (HTTP/S)
LAN host127.0.0.1
LAN port3000
LabelCheckout API — dev
Device uses HTTPSoff
Address schemeHTTPS (recommended)
Subdomainmy-api
https://mikrouter.com/user/agents/665…

Mappings

Web 0/10TCP 0/3
New mapping
Web (HTTP/S) 0/10Raw TCP 0/3

Device uses HTTPS — off. Tick it only if your dev server itself serves https:// on that port.

Require access password — off, because this one receives webhooks. Tick it for an app you are only showing to people.

Add mapping

Use 127.0.0.1 rather than localhost. On some systems localhostresolves to IPv6 first, and plenty of dev servers only listen on IPv4 — the numeric form removes the question.

3

Open the address

The mapping appears in the list with its full address. Open https://my-api.test.mikrouter.com in any browser, on any network.

If your app appears, you are done — skip to Webhooks and callbacks below. If instead you get "blocked request", "invalid host header", or a bare error page from your framework, that is step 4 and it is expected.

4

Let your framework accept the public hostname

MikRouter forwards the hostname unchanged, so your dev server is asked for my-api.test.mikrouter.com and not localhost. Several frameworks refuse a hostname they were not told about — it is a deliberate protection in them, not a fault in the tunnel. Add your address to the allow-list and restart the dev server.

The symptom is a cross origin request warning, and blocked dev-only requests such as hot reload.

next.config.ts
export default {
  allowedDevOrigins: ["my-api.test.mikrouter.com"],
};

Only the development server checks this. A production build does not use allowedDevOrigins at all.

5

Point your app at its own public address

Anything that builds an absolute URL needs to know the address the outside world uses, or it will emit links back to localhostthat only work on your desk. Set whichever your stack uses — APP_URL, SITE_URL, NEXTAUTH_URL, PUBLIC_BASE_URL— to the mapping's address.

.env.local
APP_URL=https://my-api.test.mikrouter.com

This is also what fixes OAuth sign-in during development: the redirect URL you register with the provider and the one your app generates have to be the same public address.

Webhooks and callbacks

This is the reason most developers want a tunnel at all. A payment gateway, GitHub, a messaging platform or your own second service has to call you, and it cannot call localhost. Point it at the mapping and the request lands in the code running on your machine — breakpoints, logs and all.

Paste this into the provider's dashboard
https://my-api.test.mikrouter.com/api/webhooks/stripe
FieldValue
Require access passwordoff
Address schemeHTTPS (recommended)

Because the address is stable, you register it once. Restart the dev server, reboot, come back next week — the same URL still reaches you, and there is no new address to go and paste in.

Never tick "Require access password" on a webhook mapping

The access gate is a sign-in page. A payment provider's servers cannot sign in, so every callback gets the unlock page instead of your endpoint, your code never runs, and the provider records a failed delivery. Nothing in your logs explains it, because the request never reached your app.

Leave webhook mappings open and let the provider's own signature checkbe the guard — verifying the signing secret is what that mechanism is for, and it is stronger than a shared password.

Databases, SSH and anything that is not a web page

Postgres, MySQL, Redis, an SSH session or a plain TCP service are not HTTP, so a subdomain cannot carry them. Choose Raw TCP instead and MikRouter gives you an address and port rather than a hostname.

FieldValue
KindRaw TCP
LAN host127.0.0.1
LAN port5432
Public portassigned for you

Connect to the port shown on the mapping:

Connect to the tunnelled Postgres
psql -h test.mikrouter.com -p 54321 -U postgres
SSH to the same machine
ssh -p 54322 you@test.mikrouter.com

A raw TCP mapping has no unlock page

The access password protects web mappings only. On a TCP mapping the service's own login is the only thing in front of it, on a port the whole internet can reach. Never expose a database that still has a default or empty password, and disable the mapping when you are done with it.

The public port is assigned, not chosen — and neither the port nor the kind can be edited afterwards. If you need a different one, delete the mapping and add it again.

Keeping it private

The address is public and guessable enough to be found. An unfinished app with test data, an admin panel with no login yet, or a debug toolbar showing environment variables should not be sitting there in the open.

  • Tick "Require access password" on any mapping a machine does not need to reach. MikRouter then asks for your account access password before it shows anything. Set that password first, under Agents — a protected mapping with no password set stays shut rather than falling open.
  • Switch the mapping off when you finish for the day. The toggle on the mapping takes the address offline and keeps its settings, so turning it back on tomorrow is one click and the address is unchanged.
  • Or stop the tunnel. Stopping the agent from its settings window takes every mapping on that machine offline at once.
  • One mapping per service. A front end on 5173 and an API on 8000 are two mappings and two addresses. Free and Starter allow 10 web and 3 TCP mappings per agent, Plus allows 20 and 6.

When it does not work

"Blocked request", "Invalid host header", or a framework error page

Your dev server is refusing the public hostname. That is step 4 above — add the address to your framework's allow-list and restart it.

The page never loads, and nothing appears in your dev server's log

The agent could not reach the port. Check the dev server is actually running, that the port in the mapping matches what it printed, and that the agent is installed on that same machine. If the agent is on a different computer — a Raspberry Pi, say — then 127.0.0.1 is wrong: start the dev server on 0.0.0.0and put your machine's LAN address in the mapping instead.

"Agent is offline"

The tunnel is down, not the dev server. Open the agent's settings window or local page and confirm it says connected, and that its card under Agents reads Online.

The page loads but styles, scripts or API calls fail

Something in the app is hard-coded to http://localhost:…. The page is on HTTPS, so the browser blocks those as mixed content. Step 5 is the fix — give the app its public base URL.

Hot reload keeps reconnecting

The reload socket is usually still aimed at localhostand its original port. Point your dev server's client port at 443 server.hmr.clientPort in Vite, WDS_SOCKET_PORT in Create React App.

A sign-in page appears instead of your app

That is the MikRouter access gate, so the mapping has Require access passwordticked. Enter your account access password — or untick it, which you must do if the caller is a webhook.

Where to next

No agent on your development machine yet? Install one — Windows, Mac or Linux — then come back to step 1.

For how mappings, agents and agent servers fit together generally, see the Agent Tunnel overview.