アプリは動いています。開発サーバーを起動し、 http://localhost:3000を開くと、期待どおりに動作します。ところが誰かにリンクを求められた瞬間、画面に表示されているURLが自分以外の誰にとっても何の意味も持たないことに気づきます。
VPSなしでlocalhostアプリに公開URLを与える方法は3つあり、相手が同じローカルネットワークにいる場合はさらに速い選択肢が1つあります。どれを選ぶかはツールの問題ではありません。アプリをどれだけの期間アクセス可能にしておく必要があるか、そして自分のノートPC以外の場所で動かせるかどうかの問題です。以下では、それぞれの方法、URLを取得するコマンド、そしてそのURLが機能しなくなる正確な条件を説明します。
要約
- 同じWi-Fi上の誰かに見せたい: 開発サーバーをすべてのネットワークインターフェースにバインドし、LAN IPを伝えます。数秒で準備でき、相手がネットワークを離れた瞬間に切れます。
- これから1時間、誰でも開けるリンクが必要: トンネルを実行します(
cloudflared、ngrok、localtunnel、localhost.run)。1分ほどで公開HTTPS URLが得られ、ルーターの変更は不要ですが、起動したプロセスが終了するとURLも消えます。 - ノートPCを閉じても動き続ける必要がある: アプリを無料ホスティング枠にプッシュします。アプリがノートPCから切り離される代わりに、クレジットカード、商用利用、再起動後にデータが残るかどうかといった新しいルールがついてきます。
- リクエスト時にサーバーコードが不要なアプリ: ビルドして静的な出力を静的ホストに置きます。ノートPCなしでもオンラインを維持でき、起こす必要のあるアプリプロセスもありません。ホストのアカウント条件と利用上限に収まっている限りは。
- 知っておくべきデフォルト設定が1つ:
next devとpython -m http.serverは、フラグなしですでにすべてのネットワークインターフェースで待ち受けています。開発サーバーは自分のノートPCだけのプライベートなものだと思っていたなら、それは違います。
自分のアプリにはどの方法が合うか
この4つの方法のうち3つはアプリに公開インターネットURLを与えます。最初の方法だけは自分のネットワーク内にしか届かず、そのため最速であると同時に最も制限が大きくなります。URLがどれだけ生き続ける必要があるかで並べれば、選択はほぼ自動的に決まります。
| 道 | URL取得までの時間 | 持続期間 | 止まる原因 | 向いている相手 |
|---|---|---|---|---|
| 同じネットワーク | 数秒 | 両者が同じネットワークにいる間 | 相手が別のWi-Fiに移動する | 隣の席の同僚、または自分のスマートフォン |
| トンネル | 1分ほど | プロセスが動いている間 | ノートPCを閉じる、ターミナルを終了する、プランの上限に達する | デモ、クライアントへの説明、Webhookのテスト |
| 無料ホスティング枠 | 10〜30分 | 条件付きで無期限 | スピンダウン、一時的なファイルシステム、プランの規約 | 自分が寝ている間も応答が必要なもの |
| 静的ビルド | 10〜20分 | 無期限 | リクエスト時にサーバーサイドのコードが必要になること | ビルド時に完全に生成できる、またはクライアント側で動くアプリ |
どの行が使えるかは、自分のプロジェクト内で確認できる3つの点で決まります:
- リクエスト時に自分のサーバーコードを実行する必要があるか? FlaskやFastAPIのルート、
server.jsのエンドポイント、リクエストごとのサーバーロジックがあるなら、サーバーサイドのホストが必要です。ビルド時のサーバーコードがあるだけで静的デプロイが除外されるわけではありません。Next.jsのServer Componentsはnext buildの実行中に動かせますし、静的なGETRoute Handlerはプリレンダリングできます。実行時のすべてのリクエストを静的アセットとして配信できるか、ブラウザから外部APIへ直接送れるなら、静的な道はまだ開いています。 - 保持が必要なファイルを読み書きするか? データベースファイル(
.db,.sqlite)、アップロード用フォルダ、アプリが編集するJSONファイルなどです。該当するなら、デプロイ前にホストのストレージモデルを確認してください。Renderの無料Webサービスと Koyebの無料インスタンスは一時的なローカルストレージを使い、Vercel Functionsは読み取り専用のファイルシステムに一時的な/tmp領域があるだけです。アプリのローカルディスクが残ると決めつけず、永続的な状態は耐久性のあるボリューム、データベース、オブジェクトストレージに置いてください。 - 実行時にシークレットが必要か? 最初の2つの方法では、
.envファイル内のキーはそのまま使えます。アプリがまだ自分のマシン上で動いているからです。残りの2つでは、プロバイダーの環境設定にキーを入力し直す必要があり、プッシュするリポジトリにキーが含まれていてはいけません。
自分のネットワーク内で共有する
next dev は、すでにマシンのすべてのネットワークインターフェースで待ち受けています(画面に出る 0.0.0.0 が意味するのはそれだけです)。 python -m http.serverも同じです。どちらもフラグは不要なので、いま動かしている開発サーバーには、同じWi-Fi上のスマートフォンからすでにアクセスできる可能性が高いです。
Next.jsのドキュメントでは -H がホスト名を変更するオプションとして説明されており、 デフォルトは 0.0.0.0です。Pythonのドキュメントには、このモジュールは すべてのインターフェースにバインドされる と書かれており、例外は --bind 127.0.0.1を渡した場合だけです。だからこれがlocalhostアプリを共有する最速の方法なのです。アカウントも、インストールも、デプロイも不要です。ほかの一般的な開発サーバーには明示的に指定する必要があります。
# Already listening on all interfaces. Nothing to add.
next dev
python -m http.server 8000
streamlit run app.py
# Needs the flag.
npm run dev -- --host # Vite
flask run --host=0.0.0.0
uvicorn main:app --host 0.0.0.0 # FastAPI
Viteのドキュメント: server.host のデフォルトは localhostで、CLIでは --host を、設定ファイルでは server.host: '0.0.0.0' を受け付けます。 Uvicornのデフォルトは 127.0.0.1で、FastAPIを動かしているのはUvicornなのでFastAPIもこれに含まれます。Streamlitは server.address を未設定のままにしており、設定リファレンスには これを設定するとアクセスが制限される と書かれています。そのアドレス1つに限定され、未設定なら制限なしです。
次に、相手に渡すアドレスが必要です。それはローカルネットワーク上での自分のマシンのIPであり、 localhost:
# macOS
ipconfig getifaddr en0
# Linux
hostname -I
# Windows (PowerShell)
ipconfig | findstr IPv4
相手に http://<that-address>:3000 を渡せば、それでアクセスできます。落とし穴はこの経路の形そのものにあります。そのアドレスは自分のネットワークの外では何の意味も持ちません。相手が別のWi-Fiやモバイル回線に切り替えたり、自宅に帰ったりした瞬間、リンクは死にます。
注意: コマンドは問題なく動くのに他の端末から接続できない場合、ほぼ毎回コマンドではなくOSのファイアウォールが原因です。Appleのファイアウォールのドキュメントによると、macOSはまだ許可していないアプリに対して警告を表示し、 ユーザーが対応するまで接続を拒否します。Windowsは代わりにどのネットワークプロファイルを適用するかを尋ね、 プライベートとパブリックで別々のルール を保持します。自宅やオフィスのネットワークではプライベートを選んでください。パブリックは絶対に選ばないでください。
トンネルの後ろに置く
トンネルとは、アプリと並行して動作し、公開HTTPSアドレスを与える小さなプログラムです。コマンド1つで1分ほどでlocalhostへの無料トンネルが手に入りますが、そのコマンドが終了した瞬間にURLも消えることは最初に知っておくべきです:
cloudflared tunnel --url http://localhost:3000
ルーターには何の変更もいりません。接続の向きがその理由です。自分のマシンがプロバイダーのエッジへアウトバウンド接続を開き(ブラウザがページを読み込むときに張る接続と同じ種類です)、プロバイダーはそれを開いたままにして、インバウンドのリクエストをその接続経由で押し戻します。自分側のインバウンドポートは閉じたままなので、ホテルのWi-Fi、スマートフォンのテザリング、ルーターを自分で管理できない家庭回線でも動作します。
注意: この最後のケースは、ポート転送を検討する前に60秒だけ確認する価値があります。ルーターのステータスページを開き、表示されているWAN IPを探し、「自分のIPを確認」系のサービスで得た実際のグローバルIPと比べてください。WAN IPが
100.64.0.0/10の範囲内なら、CGNATが原因である可能性が高いです。WAN IPとグローバルIPが単に異なるだけなら、上流にもう1つNAT層があることは分かりますが、それがCGNATなのか通常の二重NATなのかは判別できません。どちらにしても、このルーターだけでポート転送しても足りない可能性があります。この範囲は RFC 6598で共有アドレス空間として予約されており、ISPがアドレスを使い果たしたときにユーザーをその裏側に置くための範囲です。
各選択肢の違いは、主に最初に何を求められるかにあります。
Cloudflareのクイックトンネル は上記のコマンドそのものです。アカウント不要、ドメイン不要で、ランダムな trycloudflare.com サブドメインが割り当てられます。Cloudflareはこれを 同時処理200リクエストまでに制限し、超えると429を返し、Server-Sent Eventsには非対応で、同じドキュメント内で無料トンネルはテストと開発向けであり、本番Webサイトのデプロイ用ではないと明記しています。
ngrok は先にサインアップが必要で、その後 ngrok http 3000. 現在の無料プラン では、毎月更新されない1回限りの5ドル分の利用枠、最大3つのオンラインエンドポイント、1 GBの転送量、20,000回のHTTP/Sリクエスト、そして訪問者がクリックして通過する必要がある警告ページが付いてきます。自動割り当ての無料開発用ドメインももらえ、これは ngrokが2023年に発表したもの で、再起動のたびにURLが変わるという昔からの不満をなくすためのものです。
localtunnel はサインアップ不要で、npx以外のインストールも不要です: npx localtunnel --port 3000。ランダムなサブドメインが割り当てられ、READMEには --subdomain は名前をリクエストするだけで保証はしない.
localhost.run は何もインストールしません。OSに最初から入っているSSHクライアントを使うからです: ssh -R 80:localhost:3000 localhost.run。ドキュメントには ダウンロードは不要 と明記されており、無料ドメインにはアカウント設定も不要です。
VS Code はPortsパネルにこの機能を備えています。すでにエディタの中で暮らしているなら便利です。GitHubまたはMicrosoftでのサインインが必要で、デフォルト設定でつまずきます: 転送されたポートはプライベートなので、ポートをパブリックに切り替えるまで、訪問者はあなたのアカウントでサインインするよう求められます。(チームメイトになら問題ありません。リンクをクリックしたいだけのクライアントには役に立ちません。)
Tailscale Funnel も同じことができますが、たいてい判断を決める2つの制約があります。URLは自分のtailnetのドメイン上にしか置けず、 待ち受けできるのはポート443、8443、10000のみ.
どれを選ぶにせよ、自分が何を配ったのかをはっきり把握してください。アクセス制御のない公開トンネルでは、開発サーバーが配信するものすべてが、そのURLを持つ誰からでも到達可能です。リンクしたことのないルートや、有効にしたまま忘れているデバッグ画面も含めてです。15分のデモなら問題ありません。公開Discordに貼り付けるURLなら、話はかなり変わります。
期限切れが厄介なのは、アプリが壊れたように見えるからです。ここで紹介した手軽な方法の多くは、依然としてノートPC上で動くトンネルソフトに依存しています。 cloudflared、ngrok、localtunnel、またはlocalhost.runのSSHセッションを止めれば転送も止まります。Tailscale Funnelは --bg付きで実行した場合が例外で、Funnelの設定をバックグラウンドで維持し、再起動後も復元します。ただしノートPC自体がオフラインの間は、どれもローカルアプリを配信できません。トンネルを何日も動かし続けることはできますが、ふたを閉じるか、リクエスト上限に達するまでの話です。
トンネルはデモには正しい道具で、ホスティングには間違った道具です。その稼働時間はノートPCの稼働時間そのものです。
アプリを自分のマシンから外に出す
これは、ノートPCが支えでなくなる最初の方法であり、ツールよりも規約が重要になる最初の方法です。ここで終わりをもたらすのは時計ではありません。スピンダウン、ファイルシステムのリセット、あるいは「このアプリは無料インスタンスに置きたい種類のものではない」というプラン側の判断です。
以下の選択肢は、継続的な無料プランから短期トライアルまでさまざまです。上限内なら無期限にアプリをオンラインに保てるものもあれば、一定のトライアル期間後に停止したり、コンピューティングに有料アカウントを求めたりするものもあります。デプロイ前に課金、スリープ、ストレージのルールを確認してください。
以下の無料枠の条件は、2026年9月7日に各プロバイダーの料金ページまたはドキュメントページで確認したものです。
| プロバイダー | 商用利用 | 再起動後にデータは残るか | 落とし穴 |
|---|---|---|---|
| Netlify | 可 | 可。Netlify BlobsまたはDatabaseを使う場合 | カード不要で開始可能。Freeプランの月間クレジットを使い切ると、アップグレードしない限り次の請求サイクルまでプロジェクトが一時停止 |
| Render | 記載なし | 再起動で消える | カード不要で開始可能。15分アイドルでスリープ。無料Postgresは作成から30日で期限切れ |
| Cloudflare Pages / Workers | 記載なし | 可。KV、D1、R2、またはDurable Objectsを使う場合 | Pagesのビルドは月500回。Workersの無料枠は 1日100,000リクエスト |
| Vercel | Hobbyでは不可 | 再起動で消える | Hobbyは個人利用のみ。関数のファイルシステムは読み取り専用 |
| GitHub Pages | 不可 | 静的出力のみ | オンラインビジネス、EC、商用SaaSは不可。デプロイのタイムアウトは10分 |
| PythonAnywhere | 記載なし | あり | 無料アカウントは許可リストにある外部ホストにしか接続できない。 無料のWebアプリは1か月で期限切れ になり、更新しない限り停止する |
| Fly.io | 記載なし | 可。Fly Volumeを使う場合 | 継続的な無料枠なし。マシン2時間または7日間。トライアルのMachineは5分動作すると自動停止し、トライアルには20 GBのボリュームストレージが含まれる。トライアル終了後はカードを追加するまでアプリが停止 |
| Koyeb | 記載なし | 永続的なローカルストレージなし | カード必須。Koyebは29ドルの事前承認を取ってから取り消すが、サインアップ時にデフォルトでProが選択され、Starterに切り替えない限り日割り料金が請求される |
| Hugging Face Spaces | 記載なし | 再起動で消える | Static Spacesは無料。GradioとDockerは原則として有料プランが必要だが、条件を満たす無料の個人アカウントはZeroGPU上でGradio Spaceを最大2つホストできる |
| Railway | 記載なし | 可。アプリが付属の0.5 GBボリュームを使う場合 | 1回限り5ドルのクレジット付き30日間トライアルの後、月1ドルのリソースクレジット付き0ドルのFreeプランへ移行。カード不要 |
「記載なし」は、プロバイダー自身のページが無料枠についてその問いに答えていないという意味です。イエスでもノーでもなく、不明として扱ってください。
このうち3行は、始める前にもう一度見ておく価値があります。Fly.ioには継続的な無料枠がなく、トライアルはマシン2時間または7日間で終了します。Koyebには無料インスタンスがありますが、サインアップと課金の流れはデプロイ前に注意が必要です。Hugging FaceはStatic Spacesを無料のままにしていますが、新規のGradioとDockerのSpaceは、限定的なZeroGPUの例外を除いて有料アカウントが必要です。Railwayはもうこの注意リストには入りません。5ドルのクレジット付き30日間トライアルの後、現在は月1ドルのリソースクレジット付き0ドルのFreeプランに移行します。
次はデータの問題です。動いていたアプリが静かに壊れたアプリへ変わるのはここです。Renderの無料Webサービスは一時的なファイルシステム上で動き、ドキュメントには、そこに書き込んだものは、アップロードした画像やローカルのSQLiteデータベースも明示的に含めて、再デプロイ、再起動、スピンダウンのたびに失われるとはっきり書かれています。Vercelの関数は一時的な作業領域しかない読み取り専用ファイルシステム上で動くので、アプリが書き込むSQLiteファイルはそこでも安全ではありません。アプリが状態をファイルに保持しているなら、耐久性のあるストレージに移してください。ホスト型データベース、オブジェクトストレージ、またはプラットフォームが対応していれば永続ボリュームです。
スピンダウンは、決める前に一度体感しておく価値があります。Renderの無料枠では15分アイドルでサービスがスリープし、次のリクエストが 1分ほどかけてローディングページを表示しながら サービスを起こします。リンクをクリックした人がそれを見ることになります。ポートフォリオ作品なら肩をすくめて済みます。通話中にリンクを開くクライアントにとっては、ひどい60秒です。
PythonAnywhereの落とし穴はもっと分かりにくいものです。アイドル時のスピンダウンはありませんが、無料のWebアプリには1か月の期限があり、PythonAnywhereがメールで送る更新リンクをクリックしないと停止します。さらに無料アカウントは アウトバウンドのインターネットアクセスが制限 され、許可リストにある外部ホストにしか到達できません。そのリスト外のAPIを呼ぶアプリは、完全にオンラインのまま、許可されていないAPIへのリクエストだけがすべて失敗します(どこも落ちているように見えないので、デバッグは最悪です)。
静的サイトとして公開する
アプリの動作のどれもリクエスト時にサーバーコードを必要としないなら、静的ホストがここでの「設定したら忘れてよい」に最も近い選択肢です。ノートPCなしでオンラインを維持でき、起こされるのを待つアプリプロセスもありません。ホストのアカウント条件と利用上限は引き続き適用されます。該当するアプリは思ったより多いので、サーバーサイドのホストが必要だと決めつける前に、3つの質問の最初の1つに答えてみる価値があります。
基準は「バックエンドがあるか」よりも狭いものです。アプリが発行するすべてのリクエストが、自分の静的ファイルへ向かうか、ブラウザから直接他者のAPIへ向かうなら該当します。ブラウザからSupabaseや公開APIを呼ぶのは問題ありませんが、ブラウザコードに置いたキーは公開したキーだという1点だけは注意が必要です。扉を閉ざすのは、リクエストごとに自分のコードをサーバー上で実行する必要がある場合です。
該当するなら、手順は短いです:
npm run build # Vite writes to dist/, a Next.js static export writes to out/
その後、ビルド出力を静的ホストにデプロイします。Cloudflare Pages、Netlify、Vercelは接続したリポジトリからビルドできます。GitHub Pagesはブランチから静的ファイルを公開するか、GitHub Actionsでフレームワークのビルドを実行して生成物をデプロイできます。
GitHub Pagesの制約は、最初から考慮すべきほど明確です。静的のみ、アカウントごとにユーザーまたは組織サイトは1つ、そして ビジネス向けの無料ホスティングではない、ECサイトや商用SaaSにも使えないと明記した利用制限があります。アプリで決済を受け付けるつもりなら、始める前に除外されます。
この方法にはアプリプロセスの期限切れがありません。ホスティングアカウントが有効で上限内にある限り動き続け、アプリがリクエスト時にサーバーサイドの処理を必要とするようになった時点で合わなくなります。
無料の道が終わる場所
無料の方法は一斉にではなく、それぞれ別の場所で行き詰まります。静的ホストはすでにノートPCの稼働時間の問題を解決し、安定したプロバイダーのURLをくれます。無料のアプリホストも同じことができ、永続ストレージを含むものも出てきました。VPSが意味を持ち始めるのは要件が積み重なったときです。自分のサーバーサイドプロセスをオンラインに保つ必要があり、予測可能な永続ストレージが必要で、無料プランのリソースや利用ルールがもう合わなくなったときです。
その境界線は尊重する価値があります。デモ1回はサーバーを買う理由になりませんし、トラフィックの少ない静的プロジェクトも同様です。短期の共有にはトンネルを、アプリが本当に静的な間は静的ホストを、上限が実行内容に合っている間は無料のアプリホストを使い続けてください。
自分で管理する常時稼働のサーバーが必要になり、それに伴う運用作業を引き受ける覚悟ができたら、VPSに移行してください。 CloudzyのLinux VPS はその選択肢の1つで、他のプロバイダーの同等のVPSでも同じ役割を果たせます。最小プランで足りると決めつけず、アプリに合わせてサーバーのサイズを決めてください。
root権限、NVMe、AMD EPYCのパワーを備えたLinux VPSで開発を。
Linuxプランを見るそうすると2つのことが変わります。プラットフォームホストが提供していたgitへのプッシュによるデプロイの流れは、もう自動ではありません。 CoolifyやDokkuのようなセルフホスト型PaaSで再現するか、独自のCI/CDを構築するかですが、どちらにしてもアップデートとメンテナンスは自分の責任になります。
もう1つの変化は、そのマシンにアプリ以外も載せられることです。エディタもそこに置きたいなら、 Code ServerとClaude Codeを動かす ことも喜んで引き受けてくれます。それが嬉しいおまけになるか、丸ごと新しい週末の作業になるかは、人によります。
よくある質問
ノートPCを閉じたら公開URLが使えなくなったのはなぜ?
トンネルがアプリではなく、トンネルを作ったプロセスに紐づいていたからです。ノートPCを閉じたりターミナルを終了したりするとそのプロセスが終わり、公開URLは消えますが、アプリ自体は無事です。ツールが予約ドメインを割り当ててくれない限り、トンネルを再起動すると新しいURLになります。ノートPCがスリープしてもリンクを生かしておく必要があるなら、アプリを自分のマシンの外に出す必要があります。
ローカルアプリを公開URLに載せるにはドメイン名が必要?
ほとんどの場合、不要です。Cloudflareのクイックトンネル、ngrokが割り当てる開発用ドメイン、localtunnel、localhost.run、そして上記の無料ホスティング枠はすべて、自社ドメインのサブドメインを無料で割り当ててくれます。例外はCloudflareの名前付きトンネルで、Cloudflare DNSにすでに追加済みのドメインが必要です。
別のWi-Fiネットワークにいる人は私のローカルIPアドレスを開けますか?
いいえ。 192.168.1.42 のようなアドレスは、いま参加しているネットワーク上でたまたまそのアドレスを持っている端末を指すだけで、他のネットワークでは別の端末か、何も指しません。自分のWi-Fiの外にいる人には、代わりにトンネル、無料ホスティング枠、または静的ホストが必要です。
アプリにログインとデータベースがある場合、どの方法が使えますか?
同じネットワークとトンネルの方法は、アプリがまだ自分のマシン上で動いているのでそのまま使えます。ログインとデータベースのリクエストがブラウザからホスト型サービスへ直接送られ、リクエストごとに実行すべき非公開のサーバーサイドコードがなければ、静的デプロイも使えます。自分の認証やデータベースのコードがリクエスト時にサーバーを必要とするなら、代わりにサーバーサイドのホストを使ってください。無料のアプリホストでは、アプリのローカルファイルシステムが残ると決めつけず、永続データを耐久性のあるストレージに置いてください。

ディスカッション
コメント
ログインしてディスカッションに参加してください。