Back
Announcing: Transparent Proxy for Runloop Tunnels
Tony Deng
Developer Relations
Product

Announcing: Transparent Proxy for Runloop Tunnels

Using Runloop as the silent backend for sandbox-driven applications.

Transparent proxy routing for Runloop tunnels

If you're building a product on top of Runloop devboxes, your end users probably shouldn't see Runloop tunnel URLs. Today we're shipping a small but useful feature: a new X-Runloop-Host header that lets you front Runloop tunnels with your own domain without rewriting the request.

The problem

A lot of teams using Runloop tunnels in production want their users to see a branded URL. The user types preview-abc123.yourapp.com into their browser, the app runs in a Runloop devbox, and the tunnel hostname stays an implementation detail.

Without this feature, you have two options and they both have real problems:

  1. Redirect the user to the Runloop tunnel URL. Now tunnel.runloop.ai shows up in their browser bar, your branding is gone, and any URL you generate or share leaks infrastructure.
  2. Run your own proxy and rewrite the Host header. This usually breaks the app inside the devbox. Frameworks like Next.js, Django, and Rails do host validation by default. Cookies get scoped to the wrong domain, redirects resolve to the wrong place, and anything that reads Host to build absolute URLs starts producing garbage.

Both approaches force you to choose between branding and the app actually working. That's the gap X-Runloop-Host closes.

How it works

Your proxy adds two headers when forwarding a request to Runloop:

  • X-Runloop-Host set to the tunnel hostname you want to route to
  • Authorization: Bearer <token> for the authenticated tunnel

Runloop reads X-Runloop-Host to figure out which devbox to send the request to, strips that header before proxying, and leaves the Host header completely alone. The app inside the devbox sees the original customer-facing Host and behaves normally.

That means host validation passes, cookies get scoped correctly, and any redirect or absolute URL the app generates points back to your domain. It works the same way for HTTP and WebSocket traffic, so live-reload, HMR, and any websocket-backed features keep working too.

For the full spec of what gets routed, forwarded, and stripped, see the tunnels documentation.

Example: branded preview URLs for an AI app builder

Say you're building a product where a user describes an app and your agent spins up a working version they can click around in. Each preview runs in a Runloop devbox and you want the user to see https://preview-abc123.yourapp.com.

Here's what the request flow looks like:

  1. The end user types https://preview-abc123.yourapp.com into their browser
  2. Their browser hits your proxy (a Cloudflare Worker, an nginx server, an Express app, whatever you've deployed at that domain)
  3. Your proxy adds X-Runloop-Host: 8080-<tunnel_key>.tunnel.runloop.ai and Authorization: Bearer <token>, then forwards the request to Runloop
  4. The Runloop tunnel backend reads X-Runloop-Host, routes to the correct devbox, and strips the header
  5. The app inside the devbox sees Host: preview-abc123.yourapp.com and responds normally
  6. The response flows back through Runloop, through your proxy, to the user

A minimal Cloudflare Worker doing the forwarding looks roughly like this:

export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const tunnelHost = `8080-${env.TUNNEL_KEY}.tunnel.runloop.ai`;
    const upstream = new URL(url.pathname + url.search, `https://${tunnelHost}`);
    const headers = new Headers(request.headers);
    headers.set('X-Runloop-Host', tunnelHost);
    headers.set('Authorization', `Bearer ${env.RUNLOOP_TOKEN}`);
    return fetch(upstream, {
      method: request.method,
      headers,
      body: request.body,
      redirect: 'manual',
    });
  },
};

The end user sees only your domain, the tunnel hostname never appears in their browser, and the app inside the devbox treats the request like any normal request to your domain.

Authentication

X-Runloop-Host only works with authenticated tunnels and requires a valid bearer token, and using it against an open tunnel or without a valid token returns 401. The routing override is scoped to proxies that can prove they own the tunnel, so it can't be abused to reach tunnels you don't control.

Where to go next

X-Runloop-Host is live today and works against any authenticated tunnel. If you're using Runloop tunnel URLs directly, nothing changes for you.

For teams running tunnels behind a branded domain, this is the right time to drop any Host-rewriting workarounds you've built and let the tunnel backend handle routing cleanly. For teams evaluating Runloop tunnels for user-facing previews, sandboxes, or per-customer environments, this release closes the last gap between "tunnels work" and "tunnels work behind your own brand."

Technical details and configuration patterns will continue to evolve. Check out the latest documentation and SDK references:

Conclusion

Transparent proxy routing closes a small but real gap in production tunnel deployments: serving Runloop devboxes behind your own branded domain without the app inside the devbox losing track of what domain it's serving.

That makes preview URLs, per-customer sandboxes, and any other user-facing tunnel workflow easier to ship without compromise. Try it against an authenticated tunnel today, and reach out to Runloop if you're building something where this pattern matters.