Your API runs on 127.0.0.1 and something on the internet needs to reach it — a payment
provider sending webhooks, a colleague reviewing a feature, a mobile app on real hardware. A
tunnel gives that local server a public HTTPS address.
How a tunnel actually works
Worth understanding, because it explains both the security model and why no firewall configuration is needed.
Your machine opens an outbound connection to the tunnel server and keeps it open. The tunnel server owns a public hostname. When a request arrives at that hostname, it is pushed down the connection your machine already opened, handed to your local port, and the response travels back the same way.
Nothing on your network ever listens for inbound connections. That is why tunnels work behind NAT, on hotel wifi, and inside corporate networks that block everything inbound — and it is also why the tunnel server can see the traffic, which is the trade-off you are accepting.
Start the tunnel
- Sign in to SchemaClient. Tunneling routes traffic through our servers, so unlike the REST client it needs an account.
- Open the Tunnel tab.
- Set the target to whatever your dev server is bound to:
http://127.0.0.1:8000
- Click Start Tunnel.
- Copy the public URL that appears — it looks like
https://u1-a3f9.schemaclient.com.
Anything sent to that URL is forwarded to your local port, and the response travels back the same way.
Bind address matters
If your server is bound to 127.0.0.1 it accepts connections only from your own machine,
which is fine — the tunnel client runs there too. But if you are running the server inside
Docker, 127.0.0.1 inside the container is not your host. Bind to 0.0.0.0 in the container
and publish the port, then tunnel the published host port.
Check it works
From any machine, or just a different network:
curl https://u1-a3f9.schemaclient.com/health
If that returns what http://127.0.0.1:8000/health returns locally, the tunnel is up.
When it does not work
| Symptom | Cause | Fix |
|---|---|---|
| 502 or connection refused | Local server not running, or on a different port | Confirm the port your server actually bound to |
| Works locally, 404 through the tunnel | Framework is checking the Host header | Add the tunnel hostname to allowed hosts |
| Assets 404, page loads unstyled | App generates absolute http://localhost URLs | Find the hardcoded base URL in config |
| Infinite redirect loop | App forces HTTPS and sees the forwarded request as HTTP | Trust X-Forwarded-Proto |
| Works, then stops | Tunnel session ended | Restart; the URL may change |
The host-header one catches nearly everyone. In Django it is ALLOWED_HOSTS, in Rails
config.hosts, in Vite server.allowedHosts. The framework is doing the right thing —
rejecting a Host it was not told about — so add the hostname rather than disabling the
check.
The redirect loop is the second most common. Your app sees a plain HTTP request from the
tunnel and redirects to HTTPS; the tunnel forwards that redirect; the browser comes back;
repeat. The fix is to make the app trust the X-Forwarded-Proto header so it knows the
original request was already HTTPS.
What does and does not relay
Plain HTTP requests relay, and so do Server-Sent Events — a streaming SSE endpoint on your machine can be consumed through the tunnel, which is useful for testing streaming responses against a hosted client.
Native WebSocket and gRPC connections do not traverse the tunnel. Point those clients directly at your local server. It is worth knowing before you design a demo around it — see WebSocket testing and tunnel streaming for the detail.
Security, briefly
A tunnel is a door you are opening deliberately. Sensible practice:
- Expose one port, the dev server's — not a database, admin panel or file share.
- Never tunnel real production data. Use seeded or synthetic data.
- Treat the URL as a secret while it is live, and share it only with what needs it.
- Stop the tunnel when the session ends. An idle open tunnel is an idle open door.
- Keep verifying webhook signatures. The tunnel changes how traffic reaches you, not whether the sender is who they claim to be.
Watch the traffic
The point of tunneling through an API client rather than a bare tunnel is that every request is captured. See monitoring API traffic for filtering and replay, or go straight to testing webhooks locally, which is what most people want a tunnel for in the first place.