Run Your Entire Web Dev Workflow From Your Phone (Claude Code + a Tunnel)

Claude Code's Remote Control puts your session on your phone, but not your localhost. Here's the tunnel setup, and the dev-server config that bites.

AI Md Aminur Islam

There’s a specific kind of frustration that shows up the moment you start driving Claude Code from your phone.

The coding part works beautifully. You’re on a bus, or in a queue, or lying on the couch, and you can read what Claude is doing, approve a tool call, tell it to refactor the header, ask it to fix the failing test. Your laptop is at your desk doing the work. Your phone is the steering wheel.

Then Claude says “done - check it out at http://localhost:3000” and the whole thing falls apart. Your phone has no idea what localhost:3000 means. That address points at your phone, not your laptop. You are now a developer who can write code but cannot look at it.

The fix is two pieces stacked on top of each other: Remote Control for driving the session, and a tunnel for seeing the result. Here’s how to wire both up.


Part 1: Get Claude Code onto your phone

Claude Code has a Remote Control feature that connects a session running on your machine to the Claude mobile app. The session never leaves your laptop - your files, your dev server, your node_modules all stay put. The phone is just a window into that terminal.

Start it from your project folder:

claude --remote-control "My App"

You get a normal interactive session at your desk and the same session on your phone. If you’re already mid-conversation, /remote-control (or /rc) does the same thing without losing your history. If you’re walking away entirely, claude remote-control runs it in server mode with no local prompt.

Either way, Claude Code prints a session URL and a QR code. Scan the QR into the Claude app, or open the app, tap Code, and pick the session from the list. That’s the whole setup.

Two things worth doing while you’re at the desk:

  • Turn on push notifications. Run /config and enable pushes. Now your phone buzzes when a build finishes or Claude needs a decision, instead of you checking every few minutes. You can also ask inline: “notify me when the tests pass.”
  • Check you’re eligible. Remote Control needs a paid claude.ai plan and claude auth login - API keys won’t work. If it refuses to start, claude doctor says why.

Part 2: Why you still can’t see your app

You’ve got Claude in your pocket. You tell it to change the hero section. It does. And you’re stuck, because the dev server is bound to 127.0.0.1:5173 on a laptop that’s behind your router, behind your ISP’s NAT, with no public address.

Being on the same Wi-Fi sometimes helps - you can occasionally reach http://192.168.1.x:5173 if your dev server binds to 0.0.0.0. But that dies the moment you leave the house, and it’s exactly when you leave the house that this whole setup becomes worth doing.

What you actually want is a tunnel: a small daemon on your laptop that opens an outbound connection to a public edge server, which then forwards inbound requests back down that pipe to your local port. No firewall changes. No port forwarding. No public IP.


Part 3: Picking a tunnel

Three realistic options, and the free tiers diverge sharply.

Cloudflare Tunnel - the best free option

Two flavours, and the distinction matters more than most tutorials admit.

Quick tunnel (no account, one command):

cloudflared tunnel --url http://localhost:5173

You get a random trycloudflare.com subdomain, printed to your terminal. Copy it to your phone and you’re browsing your local app from anywhere. Zero setup.

The catch: quick tunnels are explicitly meant for development and testing. Expect a cap on in-flight requests, no Server-Sent Events support, no SLA, and a brand new URL every time you restart. For “look at my dev server for the next two hours,” that’s completely fine. For anything you want to leave running, it isn’t.

Named tunnel (account + your domain on Cloudflare DNS):

More setup - you create the tunnel, route DNS to it, and usually end up managing a config.yml or a system service. In exchange you get a stable URL on your own domain that survives restarts, free SSL, no bandwidth caps, no session timeouts, and you can put Cloudflare Access in front of it so only you can load the page. The requirement is that your domain’s authoritative DNS lives on Cloudflare.

If you’re going to do this more than twice, spend the twenty minutes on a named tunnel. dev.yourdomain.com pointing at your laptop is a genuinely nice thing to own.

ngrok - fastest to first URL, tightest free tier

ngrok http 5173

Still the quickest path from nothing to a public URL, and its request inspector is better than anything Cloudflare gives you - useful if you’re debugging webhooks rather than just eyeballing a page. Every account gets a free static domain, so your URL doesn’t rotate on restart.

But ngrok cut its free plan hard in early 2026: roughly 2-hour session limits, about 1 GB of monthly bandwidth, a handful of endpoints, and an interstitial warning page in front of every visit. The two-hour ceiling is the one that hurts here - you’ll be restarting the tunnel and re-copying URLs in the middle of your afternoon. Removing the interstitial means paying.

Expose - good if you’re already in PHP land

expose share http://localhost:8000

Beyond Code’s tool, self-hostable, and pleasant if you’re doing Laravel work. The shared free tier has session limits similar in spirit to ngrok’s. Its real selling point is that you can run your own Expose server on a cheap VPS and stop worrying about anyone’s free tier.

Quick comparison

Cloudflare quickCloudflare namedngrok freeExpose (shared)
Account neededNoYesYesYes
Setup time~1 min~20 min~2 min~2 min
Session limitNoneNone~2 hoursLimited
Stable URLNoYes (your domain)Yes (static domain)No
Bandwidth capNone statedNone~1 GB/moLimited
Interstitial pageNoNoYesNo
Best forA quick lookLeaving it runningWebhook debuggingPHP/Laravel

Recommendation: quick tunnel today because you want to get moving, named tunnel this weekend because you’ll want it permanently.


Part 4: The dev-server config that will bite you

This is the step nobody warns you about, and it produces a blank page or a scary error banner that looks like the tunnel is broken. It isn’t. Modern dev servers reject requests whose Host header doesn’t match localhost, as a defence against DNS rebinding attacks. Your tunnel’s hostname is, by definition, not localhost.

Vite - add the tunnel host to vite.config.js:

export default {
  server: {
    host: true,              // bind 0.0.0.0
    allowedHosts: ['.trycloudflare.com', 'dev.yourdomain.com'],
    hmr: { clientPort: 443 } // HMR over the tunnel's HTTPS
  }
}

That hmr.clientPort line is what makes hot reload keep working through the tunnel. Without it the page loads but never updates, and you’ll waste ten minutes wondering why Claude’s changes aren’t showing up.

Next.js - add allowedDevOrigins in next.config.js:

module.exports = {
  allowedDevOrigins: ['dev.yourdomain.com', '*.trycloudflare.com']
}

WordPress through Herd has the same shape of problem with a different cause: WordPress stores the site URL in the database, so the tunneled page loads as bare HTML while every asset still points at my-site.test.

Herd installs Expose into your PATH and gives you a token field in settings, so from the site directory the tunnel is one command:

herd share

Herd detects the site’s local URL and shares the right address. Now teach WordPress to answer on the public hostname, in wp-config.php above the “stop editing” line:

$tunnel_host = $_SERVER['HTTP_X_FORWARDED_HOST'] ?? '';

if (str_contains($tunnel_host, 'sharedwithexpose.com')) {
    $_SERVER['HTTP_HOST'] = $tunnel_host;
    $_SERVER['HTTPS']     = 'on';
    define('WP_HOME',       'https://' . $tunnel_host);
    define('WP_SITEURL',    'https://' . $tunnel_host);
    define('COOKIE_DOMAIN', $tunnel_host);
}

Keep it conditional - hardcoding WP_SITEURL to the tunnel fixes the phone and breaks local .test access. Matching the suffix rather than an exact domain means it survives the free plan’s rotating subdomains. The HTTPS line stops WordPress emitting http:// URLs onto an https:// page, since Expose terminates TLS before PHP sees the request.

Confirm the header name before trusting that snippet. Expose rewrites Host so Herd can pick the right site, which means the public hostname arrives in a forwarded header - load the tunnel on your phone, then read the actual request headers in Expose’s dashboard at http://127.0.0.1:4040 and swap the key if it differs.

URLs baked into post content still point at .test, and multisite is worse again - don’t tunnel that one.

While you’re here, tell Claude about it once and never think about it again. Drop this in your CLAUDE.md:

## Mobile preview
This project is previewed through a Cloudflare tunnel at dev.yourdomain.com.
When adding a dev server config, keep `allowedHosts` and HMR clientPort 443 intact.
Never hardcode localhost URLs in client-side code - use relative paths.

That last line matters more than it looks. A hardcoded http://localhost:8000/api in your frontend works perfectly on your laptop and fails silently on your phone, because your phone’s “localhost” is your phone.


Part 5: Putting it together

The full startup sequence, run once at your desk before you go anywhere:

# Terminal 1 - your app
npm run dev

# Terminal 2 - the tunnel
cloudflared tunnel --url http://localhost:5173

# Terminal 3 - Claude, with Remote Control on
cd ~/projects/my-app
claude --remote-control "My App"

Then: scan the QR code into the Claude app, copy the tunnel URL into your phone’s browser, and put both on your home screen or in split view. That’s the whole rig.

The loop, once you’re out of the house:

  1. Type into the Claude app. “The mobile nav overlaps the logo below 400px. Fix it.”
  2. Claude works on your laptop. You watch the tool calls stream by. Approve anything it asks about.
  3. Switch to your browser tab, pull to refresh. Or don’t - with HMR configured, it’s usually already updated.
  4. Screenshot the problem and send it back. This is the part that feels like cheating. Attach a photo or screenshot in the Claude app; Claude sees the image directly as part of your message. “Still overlapping, see attached” is a complete bug report.
  5. Repeat.

Part 6: The things that will go wrong

Your laptop must stay awake. Both the session and the tunnel are local processes - a closed lid ends the party. Disable sleep on AC power. This is a preview mechanism, not hosting; if you want the app up permanently, deploy it.

Your dev server is now public. A trycloudflare.com URL is unguessable but has no auth in front of it. Never tunnel a production database, an admin panel, or a debug endpoint, use fake data locally, and stop the tunnel when you’re done. On a named tunnel, put Cloudflare Access in front and the problem goes away.

Dropped sessions come back. Ctrl+C’d out of claude remote-control? Run it again in the same directory within a few hours and it picks up where it left off.

Set it up once. The next time you have an idea on a walk, you can just build it.