<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[tunnelto.me]]></title><description><![CDATA[tunnelto.me]]></description><link>https://blog.tunnelto.me</link><image><url>https://cdn.hashnode.com/uploads/logos/69b97935c22d3eeb8acb1452/5a7f0f5d-dc51-4280-a0ae-9f453a20d010.png</url><title>tunnelto.me</title><link>https://blog.tunnelto.me</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 04 Sep 2026 17:23:28 GMT</lastBuildDate><atom:link href="https://blog.tunnelto.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How do I expose my localhost on a custom domain for free?]]></title><description><![CDATA[The first time I shared a localhost preview with a client, I sent them an ngrok URL. They asked me, completely seriously, whether the link was a phishing attempt. Random subdomains on a service they'd]]></description><link>https://blog.tunnelto.me/how-do-i-expose-my-localhost-on-a-custom-domain-for-free</link><guid isPermaLink="true">https://blog.tunnelto.me/how-do-i-expose-my-localhost-on-a-custom-domain-for-free</guid><category><![CDATA[localhost]]></category><category><![CDATA[Custom Domain]]></category><category><![CDATA[tunnel]]></category><category><![CDATA[devtools]]></category><category><![CDATA[ngrok alternative]]></category><dc:creator><![CDATA[Alon Sabi]]></dc:creator><pubDate>Mon, 04 May 2026 16:02:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69b97935c22d3eeb8acb1452/55cc886e-69a2-4995-879a-7d41ecfcf8fc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The first time I shared a localhost preview with a client, I sent them an ngrok URL. They asked me, completely seriously, whether the link was a phishing attempt. Random subdomains on a service they'd never heard of look exactly like phishing.</p>
<p>That was the moment I decided I'd never share another <code>*.ngrok-free.app</code> or <code>*.somevendor.com</code> URL with another human. If I'm going to share a localhost preview, it's going on a real domain that actually says my name.</p>
<p>This post walks through how I do that today, using a domain I already owned, for free, in about five minutes.</p>
<blockquote>
<p><strong>Short answer:</strong> Install <a href="https://tunnelto.me">tunnelto</a>, add one CNAME record at your DNS provider, and run <code>tunnelto add preview.yourdomain.com 3000</code>. tunnelto handles certificate issuance and routing automatically. The free plan includes 1 custom domain.</p>
</blockquote>
<h2>Why do I want a custom domain on localhost in the first place?</h2>
<p>Three reasons keep coming up:</p>
<ol>
<li><p><strong>Stable URLs.</strong> Random tunnel subdomains rotate every reconnect. A URL on my own domain doesn't. That means webhook configs, OAuth callbacks, bookmarks, and shared preview links keep working forever.</p>
</li>
<li><p><strong>Trust.</strong> A URL like <code>preview.acme.com</code> looks like a real product. A URL like <code>7f3a2b9.tunnel-vendor.com</code> looks like phishing. This matters more than it should when you're sharing previews with non-technical stakeholders.</p>
</li>
<li><p><strong>No vendor lock-in.</strong> If I ever change tunnel providers, my domain stays the same. I just point the CNAME somewhere else. No dead links anywhere.</p>
</li>
</ol>
<p>The first reason is purely technical. The second is about how the link reads to a human. The third is about not painting yourself into a corner. All three are real.</p>
<h2>What's the cheapest way to get a custom domain on localhost?</h2>
<p>Three options, in increasing order of friction:</p>
<table>
<thead>
<tr>
<th>Option</th>
<th>Free?</th>
<th>Catch</th>
</tr>
</thead>
<tbody><tr>
<td><a href="https://tunnelto.me">tunnelto</a> free plan</td>
<td>✅</td>
<td>1 GB/month bandwidth, 1-hour tunnel sessions on free. Persistent tunnels from $2/mo</td>
</tr>
<tr>
<td>Cloudflare Tunnel</td>
<td>✅</td>
<td>Requires moving your domain's DNS to Cloudflare</td>
</tr>
<tr>
<td>ngrok / others paid</td>
<td>❌</td>
<td>$8+/month</td>
</tr>
</tbody></table>
<p>I use tunnelto because it works with whatever DNS provider I already have. My domains are at Namecheap and Porkbun, so the Cloudflare option would mean transferring DNS just to put a tunnel on a subdomain, and that's not a tradeoff I'm willing to make for a dev setup.</p>
<p>The free plan is genuinely free. No credit card. The 1-hour tunnel session limit on free is fine for active development. If you want a tunnel that stays up forever (for staging, internal services, or webhook receivers you can't be there to restart), the Hobbyist plan is $2/month.</p>
<h2>How do I set this up step by step?</h2>
<h3>Step 1: Which subdomain should I use?</h3>
<p>Pick a subdomain on a domain you already own. Common patterns:</p>
<ul>
<li><p><code>dev.yourdomain.com</code> for general localhost development</p>
</li>
<li><p><code>preview.yourdomain.com</code> for client previews</p>
</li>
<li><p><code>hooks.yourdomain.com</code> for webhook receivers</p>
</li>
<li><p><code>api.yourdomain.com</code> for local API testing</p>
</li>
</ul>
<p>If you don't own a domain yet, grab one. They're cheap and you'll use it for everything else too. Any registrar works (Namecheap, Porkbun, Route 53, GoDaddy, Cloudflare, whoever).</p>
<p>For this guide I'll use <code>preview.acme.com</code>. Substitute your actual subdomain everywhere.</p>
<h3>Step 2: How do I install tunnelto?</h3>
<p><a href="https://tunnelto.me">tunnelto</a> is a free localhost tunnel with 1 custom domain included on the free plan.</p>
<p><strong>macOS / Linux:</strong></p>
<pre><code class="language-bash">curl -sf https://app.tunnelto.me/install.sh | sh
</code></pre>
<p><strong>Windows (via Scoop):</strong></p>
<pre><code class="language-powershell">scoop bucket add tunnelto https://github.com/asabi/scoop-tunnelto
scoop install tunnelto
</code></pre>
<p>Run <code>tunnelto</code> once after installing and follow the interactive setup. Email-based, takes about 30 seconds.</p>
<h3>Step 3: How do I point my subdomain at tunnelto?</h3>
<p>In your DNS provider's dashboard, add one CNAME record:</p>
<table>
<thead>
<tr>
<th>Type</th>
<th>Name</th>
<th>Target</th>
<th>TTL</th>
</tr>
</thead>
<tbody><tr>
<td>CNAME</td>
<td>preview</td>
<td>(tunnelto's target)</td>
<td>300</td>
</tr>
</tbody></table>
<p>The exact target value comes from tunnelto in the next step. There's nothing to copy or paste manually before that.</p>
<h3>Step 4: How do I create the tunnel?</h3>
<p>Start your local server (let's say on port 3000), then run:</p>
<pre><code class="language-bash">tunnelto add preview.acme.com 3000
</code></pre>
<p>Within a few seconds you should see:</p>
<pre><code class="language-plaintext">✓ Tunnel Created: https://preview.acme.com -&gt; localhost:3000
✓ SSL certificate will be automatically provisioned on first request.
</code></pre>
<p>That's it. <code>https://preview.acme.com</code> is now a public HTTPS URL that forwards every request to <code>localhost:3000</code> on your machine. The certificate is real (Let's Encrypt) and automatically renewed.  </p>
<p>Note: The first load can take a couple of seconds as it provisions your SSL connection.</p>
<h3>Step 5: How do I keep the tunnel running across reboots?</h3>
<p>If you want the tunnel to stay up automatically, install tunnelto as a system service (the install does it automatically, but just in case):</p>
<pre><code class="language-bash">tunnelto install-service
</code></pre>
<p>This registers tunnelto with launchd on macOS, systemd on Linux, or Windows Service. After that, <code>tunnelto start</code>, <code>stop</code>, <code>restart</code>, and <code>status</code> manage it like any other service. The tunnel survives reboots, terminal closes, and laptop sleeps.</p>
<h2>What if I want multiple subdomains on the same domain?</h2>
<p>Two ways to handle this:</p>
<p><strong>Option 1: Multiple custom domains.</strong> Paid plans on tunnelto include more custom domains (3 on Plus, 5 on Pro, up to 100 on Enterprise). Each one is configured independently.</p>
<p><strong>Option 2: Wildcard DNS.</strong> tunnelto supports wildcard DNS for custom domains. Configure <code>*.dev.acme.com</code> once, and any subdomain like <code>feature-x.dev.acme.com</code>, <code>staging.dev.acme.com</code>, or <code>branch-42.dev.acme.com</code> routes to your local server. Useful for multi-tenant testing, branch previews, and per-feature subdomains. This counts as a single custom domain.</p>
<p>For most solo developers, the free plan's 1 custom domain plus a wildcard configuration covers everything you'd want.</p>
<h2>How do I handle WebSockets, Postgres, or other non-HTTP services?</h2>
<p>tunnelto isn't HTTP-only. A few extra options worth knowing:</p>
<p><strong>WebSockets:</strong> Set the tunnel's TLS termination to <code>server-tls</code> in the dashboard. In this mode, tunnelto terminates TLS at the edge and forwards raw TCP to your local service, so the WebSocket upgrade passes through transparently. <code>wss://</code> connections then work through the tunnel like any other HTTPS request.</p>
<p><strong>Raw TCP:</strong> Postgres, Redis, SSH, MQTT, game servers, and anything else that speaks raw TCP. Enable "Allow External TCP" on the tunnel from the dashboard. tunnelto allocates a port and exposes it directly.</p>
<p><strong>Static files only, no local server:</strong> Pass a directory path instead of a port. <code>tunnelto add demo.acme.com /path/to/build</code> serves the directory contents directly. Useful for sharing static site builds and design mockups without spinning up Nginx or a Vite preview.</p>
<p><strong>Forward to a different machine on your LAN:</strong> Pass <code>host:port</code> instead of just a port. <code>tunnelto add api.acme.dev 192.168.1.50:8080</code> forwards traffic to whichever machine you specify. Useful for tunneling to a Raspberry Pi, Docker host, or VM. The tunnelto client process opens the connection, so whichever machine runs the client must be able to reach the target on its LAN.</p>
<h2>Will SSL just work?</h2>
<p>Yes. tunnelto provisions a real Let's Encrypt certificate the moment your DNS verification succeeds. There's nothing to copy, no certbot to run, and no renewal cron job to forget about. Browsers trust it like any other public site.</p>
<p>If you ever change DNS providers or add new subdomains, tunnelto re-verifies and re-issues certificates automatically.</p>
<h2>Does this work for OAuth callbacks?</h2>
<p>Yes, and this is one of the most common reasons to want a custom domain on localhost in the first place. OAuth providers (Google, GitHub, Microsoft, Auth0, Okta) require a stable redirect URI. If your tunnel URL changes every reconnect, every OAuth provider stops trusting your callback and the login flow silently breaks.</p>
<p>With a custom domain, you set the OAuth redirect URI once and forget about it. <code>https://preview.acme.com/auth/callback</code> is yours forever.</p>
<p>A small gotcha: some OAuth providers (Google in particular) won't let you use a custom subdomain for OAuth unless you've verified ownership of the parent domain. Verifying takes a few minutes via TXT record, and once it's done, every subdomain is fair game.</p>
<h2>Where do I go from here?</h2>
<p>The whole point of putting localhost on a custom domain is to stop thinking about the tunnel URL. It becomes invisible plumbing. Webhook configs, OAuth callbacks, shared previews, and bookmarks all keep working forever.</p>
<p>If you haven't installed tunnelto yet, take 60 seconds to <a href="https://app.tunnelto.me/auth/login">create your free account</a>. The free plan is permanent, includes 1 custom domain, and doesn't ask for a credit card. You can read more about the <a href="https://tunnelto.me/localhost-custom-domain.html">custom domain use case here</a>, or jump straight to the <a href="https://tunnelto.me">tunnelto homepage</a>.</p>
<p>Happy tunneling.</p>
]]></content:encoded></item><item><title><![CDATA[How do I migrate from ngrok to a free alternative? (with custom domains)]]></title><description><![CDATA[I used ngrok for years. It does the job. But three things kept biting me, and eventually they were enough that I went looking for an alternative:

Every reconnect handed me a new random subdomain, whi]]></description><link>https://blog.tunnelto.me/how-do-i-migrate-from-ngrok-to-a-free-alternative-with-custom-domains</link><guid isPermaLink="true">https://blog.tunnelto.me/how-do-i-migrate-from-ngrok-to-a-free-alternative-with-custom-domains</guid><category><![CDATA[ngrok alternative]]></category><category><![CDATA[ngrok]]></category><category><![CDATA[localhost]]></category><category><![CDATA[devtools]]></category><category><![CDATA[tunnel]]></category><dc:creator><![CDATA[Alon Sabi]]></dc:creator><pubDate>Thu, 09 Apr 2026 21:28:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69b97935c22d3eeb8acb1452/c9e72eef-63e8-4350-a64d-f8a5f7ef036f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I used ngrok for years. It does the job. But three things kept biting me, and eventually they were enough that I went looking for an alternative:</p>
<ol>
<li><p>Every reconnect handed me a new random subdomain, which broke every webhook config and OAuth callback I'd set up the day before.</p>
</li>
<li><p>The free tier capped me at one tunnel and one hour of session time before forcing a reconnect.</p>
</li>
<li><p>Putting a real domain on a tunnel cost money. Real money. Even though my domain was already paid for.</p>
</li>
</ol>
<p>The fix turned out to be simpler than I thought. This post walks through exactly how I migrated, what changed in my workflow, and what to do if you've already wired ngrok URLs into Stripe, GitHub, OAuth providers, or anywhere else.</p>
<blockquote>
<p><strong>Short answer:</strong> Install <a href="https://tunnelto.me">tunnelto</a>, point a subdomain you own at it, and update your old ngrok URLs to the new one. Total time is about 10 minutes for the first tunnel and 30 seconds for each one after.</p>
</blockquote>
<h2>Why am I leaving ngrok?</h2>
<p>Most developers using ngrok bump into the same wall. The free tier is good enough until you start integrating with a real third-party service. Then the random subdomain becomes a problem you can't ignore.</p>
<p>Stripe stores the webhook URL in your account. So does GitHub. So does Slack. So do all your OAuth providers (Google, Microsoft, GitHub, Auth0). Every time the ngrok URL rotates, you have to log into each of those dashboards and update the URL by hand. Forget one and you spend an hour debugging "why are my webhooks failing" before you remember.</p>
<p>There are paid plans that give you a stable subdomain or a custom domain, but they start around $8 per month. That's not a huge amount in absolute terms, but it's a lot for a feature that is essentially "give me a string that doesn't change."</p>
<p>What I actually want is:</p>
<ul>
<li><p>A URL on a domain I already own</p>
</li>
<li><p>That stays the same forever</p>
</li>
<li><p>For free</p>
</li>
<li><p>With HTTPS</p>
</li>
<li><p>Without a credit card</p>
</li>
</ul>
<p>That's a real list of requirements, and it's the reason I went looking for an alternative.</p>
<h2>What's the best free alternative to ngrok?</h2>
<p>I tried four. Here's the honest comparison:</p>
<table>
<thead>
<tr>
<th></th>
<th>ngrok Free</th>
<th>tunnelto Free</th>
<th>Cloudflare Tunnel</th>
<th>localhost.run</th>
</tr>
</thead>
<tbody><tr>
<td>Custom domain on free</td>
<td>❌</td>
<td>✅ 1 included</td>
<td>✅ Cloudflare DNS only</td>
<td>❌</td>
</tr>
<tr>
<td>Stable URL across reconnects</td>
<td>❌</td>
<td>✅ Your domain</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>Works with any registrar</td>
<td>N/A</td>
<td>✅</td>
<td>❌ Cloudflare DNS required</td>
<td>N/A</td>
</tr>
<tr>
<td>HTTPS by default</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>No credit card to start</td>
<td>✅</td>
<td>✅</td>
<td>Cloudflare account</td>
<td>✅</td>
</tr>
<tr>
<td>Native binary install</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>SSH only</td>
</tr>
</tbody></table>
<p>I landed on <a href="https://tunnelto.me">tunnelto</a> because it was the only one that gave me a custom domain on the free plan <strong>with any registrar</strong>, not just Cloudflare. My domains live at Namecheap and Porkbun, so the Cloudflare option would have meant transferring DNS just to get a tunnel. tunnelto just works with whatever I already have.</p>
<p>That's the rest of this post: how to actually do the migration.</p>
<h2>How do I migrate from ngrok to tunnelto?</h2>
<p>The migration is six steps. The whole thing takes about 10 minutes the first time.</p>
<h3>Step 1: How do I install tunnelto?</h3>
<p><a href="https://tunnelto.me">tunnelto</a> is a free localhost tunnel that includes 1 custom domain on the free plan. No credit card required.</p>
<p><strong>macOS / Linux:</strong></p>
<pre><code class="language-bash">curl -sf https://app.tunnelto.me/install.sh | sh
</code></pre>
<p><strong>Windows (via Scoop):</strong></p>
<pre><code class="language-powershell">scoop bucket add tunnelto https://github.com/asabi/scoop-tunnelto
scoop install tunnelto
</code></pre>
<p>Sign up at <a href="https://app.tunnelto.me">app.tunnelto.me</a> to get your access token, then run <code>tunnelto</code> once and paste the token when prompted. Takes about 30 seconds.</p>
<h3>Step 2: Which subdomain should I use to replace my ngrok URL?</h3>
<p>Use a subdomain on a domain you already own. My personal pattern is:</p>
<ul>
<li><p><code>dev.mydomain.com</code> for general localhost development</p>
</li>
<li><p><code>hooks.mydomain.com</code> for webhook receivers</p>
</li>
<li><p><code>preview.mydomain.com</code> for client previews</p>
</li>
</ul>
<p>For this guide I'll use <code>dev.acme.com</code>. Substitute your real domain everywhere below.</p>
<h3>Step 3: How do I point my subdomain at tunnelto?</h3>
<p>Add the parent domain (<code>acme.com</code>) in your tunnelto dashboard. The dashboard then shows you two records to add at your DNS provider (Namecheap, Porkbun, GoDaddy, Route 53, anywhere your domain lives):</p>
<table>
<thead>
<tr>
<th>Type</th>
<th>Name</th>
<th>Value</th>
<th>TTL</th>
</tr>
</thead>
<tbody><tr>
<td>TXT</td>
<td><code>_tunnelto-verify.acme.com</code></td>
<td>(verification token from dashboard)</td>
<td>300</td>
</tr>
<tr>
<td>A</td>
<td><code>*.acme.com</code></td>
<td>(tunnelto server IP from dashboard)</td>
<td>300</td>
</tr>
</tbody></table>
<p>The TXT record proves you own the domain. The wildcard A record lets tunnelto serve any subdomain under it (<code>dev.acme.com</code>, <code>hooks.acme.com</code>, <code>preview.acme.com</code>, etc.) — that's why you only need one custom domain to cover the whole pattern.</p>
<blockquote>
<p><strong>Tip:</strong> Many DNS providers auto-append the parent domain when you enter the Name field — if so, enter only <code>_tunnelto-verify</code> and <code>*</code> in the Name field.</p>
</blockquote>
<p>DNS changes can take a few minutes to propagate. tunnelto checks both records automatically every 5 minutes, or you can click <strong>Verify DNS</strong> in the dashboard to check immediately.</p>
<h3>Step 4: How do I create the tunnel?</h3>
<p>Start your local server (let's say on port 3000), then run:</p>
<pre><code class="language-bash">tunnelto add dev.acme.com 3000
</code></pre>
<p>You'll see:</p>
<pre><code class="language-plaintext">Tunnel created: https://dev.acme.com -&gt; 127.0.0.1:3000
SSL certificate will be automatically provisioned on first request.
</code></pre>
<p>That's it. <code>https://dev.acme.com</code> is now a public HTTPS URL forwarding to <code>localhost:3000</code>. The first incoming request triggers Let's Encrypt to issue a real certificate; renewals happen automatically. The URL is yours and it doesn't rotate.</p>
<h3>Step 5: How do I update my old ngrok URLs?</h3>
<p>This is the part that takes most of the time, and it's also the part that you only do once. Here's a checklist by service:</p>
<p><strong>Stripe</strong></p>
<ul>
<li><p>Dashboard → Developers → Webhooks → click your endpoint → "..." menu → Update details → paste the new URL</p>
</li>
<li><p>The signing secret stays the same. Don't regenerate it.</p>
</li>
</ul>
<p><strong>GitHub webhooks</strong></p>
<ul>
<li><p>Repo or org Settings → Webhooks → click the webhook → Payload URL field → save</p>
</li>
<li><p>The webhook secret stays the same.</p>
</li>
</ul>
<p><strong>Slack apps</strong></p>
<ul>
<li><p>api.slack.com → your app → Event Subscriptions → Request URL field</p>
</li>
<li><p>Slack will re-verify the URL when you save. tunnelto handles the verification challenge correctly.</p>
</li>
</ul>
<p><strong>OAuth providers (Google, GitHub, Microsoft, Auth0, etc.)</strong></p>
<ul>
<li><p>Find the redirect URI list in the OAuth client config</p>
</li>
<li><p>Replace the ngrok URL with your new tunnelto URL</p>
</li>
<li><p>This is the most commonly forgotten step. Your OAuth flow will silently fail if you skip it.</p>
</li>
</ul>
<p><strong>Twilio</strong></p>
<ul>
<li><p>Phone number config → Voice/Messaging webhook URL fields</p>
</li>
<li><p>Update both incoming SMS and incoming voice if you use both.</p>
</li>
</ul>
<p><strong>Shopify app webhooks</strong></p>
<ul>
<li>Apps → your app → API Configuration → webhook subscription URLs</li>
</ul>
<p><strong>Discord interactions</strong></p>
<ul>
<li>Application → General Information → Interactions Endpoint URL</li>
</ul>
<p><strong>Anything else</strong></p>
<ul>
<li>Search your codebase for <code>.ngrok-free.app</code> and <code>.ngrok.app</code> to catch hardcoded URLs. There are usually a few.</li>
</ul>
<h3>Step 6: How do I make sure my tunnel stays up?</h3>
<p>One more upgrade over the ngrok workflow: tunnelto can run as a background system service so your tunnel stays up across reboots and terminal closes. Run this once:</p>
<pre><code class="language-bash">tunnelto install-service
</code></pre>
<p>After that, <code>tunnelto start</code>, <code>stop</code>, <code>restart</code>, and <code>status</code> manage it just like any other launchd/systemd/Windows service. No more "I came in this morning and forgot to start the tunnel before triggering the test webhook."</p>
<h2>What if I'm using ngrok features that tunnelto doesn't have?</h2>
<p>Honest answer: ngrok has a richer free request inspector UI than tunnelto does, and ngrok's traffic policy / rules engine is more powerful if you're doing complex header rewrites or rate limiting at the tunnel layer. If those features are core to your workflow, you may want to keep both around.</p>
<p>For the 80% case (running a local dev server, receiving webhooks, sharing previews, supporting OAuth callbacks), tunnelto covers everything ngrok does and adds the custom-domain-on-free piece that ngrok charges for.</p>
<p>A few specific notes:</p>
<ul>
<li><p><strong>WebSockets</strong> work, with one configuration step. See the <a href="https://tunnelto.me/#faq">WebSocket FAQ</a> on the homepage.</p>
</li>
<li><p><strong>Raw TCP</strong> (Postgres, Redis, SSH, MQTT, game servers) is supported. Configure it per tunnel from the dashboard.</p>
</li>
<li><p><strong>Bearer + Basic Auth</strong> can be enabled together on a single tunnel. Both credentials are bcrypt-hashed at rest, and neither is forwarded upstream.</p>
</li>
<li><p><strong>Static file hosting</strong> without a local server: pass a directory path instead of a port.</p>
</li>
<li><p><strong>LAN host targets</strong>: pass <code>host:port</code> to forward to a Pi, Docker host, or VM on your network.</p>
</li>
</ul>
<h2>Will my OAuth and webhook secrets break when I migrate?</h2>
<p>No. Webhook signing secrets and OAuth client secrets are tied to the third-party service's account, not to the tunnel URL. When you swap the URL in the third party's dashboard, the secret stays the same.</p>
<p>The only thing that changes is the URL itself. Your application code that verifies signatures (Stripe, GitHub, Slack) doesn't need to change at all. tunnelto forwards the raw request body and headers unchanged, so signature verification works exactly the same way.</p>
<p>The one place to be careful: if you have the ngrok URL hardcoded somewhere in your code or config (not just stored in a third-party dashboard), grep for it before you decommission ngrok. Common spots:</p>
<pre><code class="language-bash">git grep -i "ngrok"
</code></pre>
<p>Common offenders include:</p>
<ul>
<li><p>Local <code>.env</code> files with <code>WEBHOOK_BASE_URL</code> or similar</p>
</li>
<li><p>Test fixtures that hit the tunnel URL</p>
</li>
<li><p>README setup instructions</p>
</li>
<li><p>CI environment variables</p>
</li>
</ul>
<h2>What do I do with my ngrok account?</h2>
<p>Once everything is migrated and you've left a few days for any forgotten integration to surface, you can either downgrade ngrok to the free tier (if you were paying) or delete the account entirely. I downgraded mine first and let it sit unused for a month before deleting it, just to be safe.</p>
<h2>Where do I go from here?</h2>
<p>The whole point of the migration is that you stop thinking about the tunnel URL. It becomes a permanent, free fixture of your dev setup, like your shell prompt.</p>
<p>If you haven't installed tunnelto yet, take 60 seconds to <a href="https://app.tunnelto.me/auth/login">create your free account</a>. The free plan is permanent, includes 1 custom domain, and doesn't ask for a credit card. You can read more about the <a href="https://tunnelto.me/ngrok-alternative.html">ngrok alternative use case here</a>, or jump straight to the <a href="https://tunnelto.me">tunnelto homepage</a>.</p>
<p>Happy tunneling.</p>
]]></content:encoded></item></channel></rss>