Tutorial
15 min read
24 views

高度なフロントエンドアーキテクチャ & DX: より高速な開発ループのためのLocalhost Tunnelsの活用

localhostトンネルを使った開発者体験の最適化方法を紹介します。ビジュアルリグレッションパイプラインの構築、フラグの同期、マイクロフロントエンドのローカルテストを学びましょう。

IT
InstaTunnel Team
Published by the InstaTunnel team | Editorial policy
高度なフロントエンドアーキテクチャ & DX: より高速な開発ループのためのLocalhost Tunnelsの活用

Quick answer

高度なフロントエンド用ローカルホストトンネルパイプラインの構築: localhost tunnel answer

A localhost tunnel gives your local app a public HTTPS URL without opening router ports, which is useful for demos, QA, mobile testing, and provider callbacks.

How do I expose localhost without opening ports?

Use a reverse HTTPS tunnel. Your machine connects outbound to the tunnel service, and the public URL forwards requests back to your local app.

When should I use a localhost tunnel?

Use one for webhook testing, OAuth callbacks, client demos, QA previews, mobile device checks, and short-lived development reviews.

フロントエンドのフィードバックループは、各チェックにデプロイが必要なため遅くなります。プレビュー環境やCIパイプラインを待つことなく、電話でのビジュアル変更やリアルシェルに対するマイクロフロントエンドのテストを行うには、摩擦とコストが伴います。

ローカルホストトンネルは、そのループを短縮する一つの方法です。マシン上の小さなエージェントがリレーにアウトバウンド接続を行い、リレーはあなたのローカルサーバーに公開HTTPS URLを提供します。一般的な選択肢は ngrok、Cloudflare Tunnel (cloudflared)、localtunnel です。それぞれのトレードオフは異なり、以下のパターンの適用範囲を決定します。

この記事では、3つのパターンと、それぞれにおいてトンネルが役立つ場面とそうでない場面を解説します:

  1. Playwrightによるビジュアルリグレッションテストをトンネル経由の開発サーバーに対して実行
  2. 実機デバイス上でのフラグのテスト
  3. デプロイ済みシェル内のマイクロフロントエンドリモートの上書き(Shadow DOMによる隔離)

トンネルの選択: ドキュメントの見解(2026年10月)

トンネルを使ったワークフローを設計する前に、これらの制限を確認してください。

ツール 安定URL 重要な制限 最適な用途
ngrok (無料) 割り当てられた開発用ドメイン(例: your-name.ngrok-free.app); 名前は選べません HTMLブラウザトラフィックにインタースティシャルページ表示; カスタムまたは予約ドメインは有料プラン必要 デバイステスト、Webhook、短いデモ
Cloudflare Quick Tunnel いいえ: 毎回新しい trycloudflare.com ホスト名 SLAなし、トンネルあたり200リクエスト、Server-Sent Events非対応 一時的なHTTPアプリの共有
Cloudflare Named Tunnel はい、自ドメイン上 Cloudflareアカウントとドメイン設定必要 長期安定エンドポイント
localtunnel ランダムサブドメイン パッケージ非アクティブ(最新npmリリースは約3年前); パブリックインスタンスは最善努力 迅速な実験や低リスクテスト

CloudflareはQuick Tunnelsをテスト専用とし、同時リクエストの上限とSSE非サポートを明記しています。あなたのアプリがSSEをストリーミングする場合(開発ダッシュボードやAIチャットUIなど)、Quick Tunnelはそれを破壊します。名前付きトンネルや他のプロバイダーを使用してください。

ngrokの無料プランでは、HTMLブラウザトラフィックの前にインタースティシャルページが挿入され、フィッシング防止策として機能します。受け入れると7日間持続するクッキーが設定されます。ngrok-skip-browser-warningヘッダー(任意の値)を含むリクエストや有料プランではこれをバイパスします。後で重要になる点として、ページが読み込むスクリプトやアセットは、期待したファイルの代わりにインタースティシャルHTMLを受け取る可能性があります。


1. トンネル経由の開発サーバーに対するビジュアルリグレッションテスト

Playwrightはexpect(page).toHaveScreenshot()によるビジュアル比較機能を標準搭載しています。古いtoMatchSnapshot()も動作しますが、PlaywrightのドキュメントはスクリーンショットにはtoHaveScreenshot()を推奨しています。

トンネルが必要な場合と不要な場合

Playwrightがあなたの開発サーバーと同じマシン(またはCIランナー)上で動作している場合、localhostに直接アクセス可能です。PlaywrightのwebServer設定はサーバーの起動も可能です。ここにトンネルは不要です。

PercyやChromaticなどのクラウドビジュアルサービスも、あなたのマシンにアクセスする必要はありません。Playwrightとの連携(@percy/playwrightとpercy exec、@chromatic-com/playwright)は、ローカルテスト中のUIのスナップショットやアーカイブをキャプチャし、比較・レビューのためにアップロードします。Percyはこれらのスナップショットを自社クラウドにレンダリングします。

リモートのブラウザグリッドや実機クラウド、作業中のブランチのレビューなど、リモート側に何かをロードさせる必要がある場合にトンネルは役立ちます。以下の例はその仕組みを示しています。同じパターンは、リモートブラウザがローカルではなくリモートの場合にも適用可能です。

実装例: Playwright + 公式ngrok SDK

公式のNode SDKは@ngrok/ngrokです。別のバイナリは不要で、ngrok.forward()を使ってリスナーを起動します。

npm install --save-dev @playwright/test @ngrok/ngrok
npx playwright install

playwright.config.ts

import { defineConfig } from '@playwright/test';

const { PLAYWRIGHT_BASE_URL, TUNNEL_USER, TUNNEL_PASS } = process.env;

export default defineConfig({
  testDir: './tests/visual',
  use: {
    baseURL: PLAYWRIGHT_BASE_URL ?? 'http://localhost:3000',
    // テストブラウザからngrokの無料プランのインタースティシャルをスキップ
    extraHTTPHeaders: { 'ngrok-skip-browser-warning': '1' },
    // トンネル設定のBasic認証に応答
    ...(TUNNEL_USER && TUNNEL_PASS
      ? { httpCredentials: { username: TUNNEL_USER, password: TUNNEL_PASS } }
      : {}),
  },
  expect: {
    toHaveScreenshot: { maxDiffPixelRatio: 0.02 }, // 差異ピクセル比率最大2%
  },
});

run-visual-tests.mjs

import ngrok from '@ngrok/ngrok';
import { spawnSync } from 'node:child_process';

const port = Number(process.env.PORT ?? 3000);
const { TUNNEL_USER, TUNNEL_PASS } = process.env;

const listener = await ngrok.forward({
  addr: port,
  authtoken_from_env: true,                        // NGROK_AUTHTOKENを読む
  basic_auth: `${TUNNEL_USER}:${TUNNEL_PASS}`,     // パブリックな開発サーバーを公開しないため
});

try {
  console.log(`トンネル稼働中: ${listener.url()}`);
  const result = spawnSync('npx', ['playwright', 'test'], {
    stdio: 'inherit',
    env: { ...process.env, PLAYWRIGHT_BASE_URL: listener.url() },
  });
  process.exitCode = result.status ?? 1;
} finally {
  await listener.close(); // 常にトンネルを閉じる
}

tests/visual/dashboard.spec.ts

import { test, expect } from '@playwright/test';

test('ダッシュボードはベースラインと一致', async ({ page }) => {
  await page.goto('/dashboard');

  // ネットワークアイドルではなく、見える要素を待つ
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();

  await expect(page).toHaveScreenshot('dashboard.png', {
    fullPage: true,
    mask: [page.locator('.timestamp-badge')], // 変動する領域をマスク
  });
});

一般的な例からの3つの変更点:

  • waitForLoadState('networkidle')に頼らない。 PlaywrightのAPIドキュメントでは、その状態はテストには推奨されず、toBeVisible()などのWebアサーションを推奨しています。
  • 変動するコンテンツは非表示にせずマスク。 toHaveScreenshot()のmaskオプションは、タイムスタンプやアバターなどの変動領域に対応します。スクリーンショットアサーションはアニメーションをデフォルトで無効化し、2回連続のキャプチャが一致するまで待ちます。
  • ベースラインの環境を一定に保つ。 OS間でフォントやアンチエイリアシングが異なるため、同じ環境(コンテナイメージの固定など)で生成・比較します。意図的にnpx playwright test --update-snapshotsで更新してください。

トンネルができることとできないこと

  • メリット: 公開HTTPS URLを通じて、ツールがあなたのマシンを見られない場合でもアクセス可能にします。
  • デメリット: これだけではテストのフレークを決定論的にしません。アニメーション、フォント、ライブデータのフレークは、マスクと安定したフィクスチャで解決します。
  • コスト: 無料プランのインタースティシャル、リクエスト制限、URLを知っている誰でもアクセス可能な点です。

2. 実機デバイス上でのフラグのテスト

実機のiPhoneやAndroid端末でのフラグ挙動は、タッチ操作、ビューポートのクイーク、WebViewの挙動、ネットワーク条件など、デスクトップエミュレーションと異なります。トンネルを使えば、何もデプロイせずにローカルビルドを端末からアクセス可能にします。

トンネルが運ぶものと運ばないもの

これは誤解しやすいポイントです。トンネルはあなたのアプリのHTMLとJavaScriptを端末に提供します。フラグの値は別の経路を通ります:フラグSDKは端末からベンダーのクラウドに直接通信し、トンネルは関係ありません。コンソールでフラグを変更しても、トンネルを通す必要はありません。トンネルは、あなたのサーバーを経由してフラグトラフィックをプロキシする場合にのみ重要です。

ページの反応速度はSDKに依存します:

  • LaunchDarklyのJavaScriptクライアントSDKは、ポーリングエンドポイントを通じて最初の接続を行い、一部(React Web SDKなど)はデフォルトでストリーミング接続を開きます。client.on('change:flag-key', …)で変更を購読します。
  • PostHogのJSライブラリは、ページごとにフラグを背景で取得し、値をキャッシュします。更新はposthog.reloadFeatureFlags()で行います。onFeatureFlagsを使って最初のフェッチ完了を待ちます。

例:LaunchDarklyのフラグ変更に反応

// useFlag.ts
import { useEffect, useState } from 'react';
import { initialize } from 'launchdarkly-js-client-sdk';

const client = initialize(import.meta.env.VITE_LD_CLIENT_ID, {
  kind: 'user',
  key: 'dev-device-01',
});

export function useFlag(key: string, fallback = false) {
  const [value, setValue] = useState(fallback);

  useEffect(() => {
    let active = true;
    const refresh = () => active && setValue(client.variation(key, fallback));

    client.waitForInitialization(5).then(refresh).catch(() => {});
    client.on(`change:${key}`, refresh);

    return () => {
      active = false;
      client.off(`change:${key}`, refresh);
    };
  }, [key, fallback]);

  return value;
}
export function Checkout() {
  const newCheckout = useFlag('new-checkout-v2');
  return newCheckout ? <ExpressCheckout /> : <LegacyCheckout />;
}

開発またはテスト環境のクライアントIDを使い、フラグの切り替えが本番ユーザーに影響しないようにします。

例:PostHogの明示的リフレッシュ

import posthog from 'posthog-js';

posthog.init(import.meta.env.VITE_POSTHOG_KEY, {
  api_host: 'https://us.i.posthog.com', // EUの場合は https://eu.i.posthog.com
});

posthog.onFeatureFlags(() => {
  console.log('new-checkout-v2:', posthog.getFeatureFlag('new-checkout-v2'));
});

// フラグ変更後に最新値を取得(ボタンやinterval、visibilitychangeから呼び出し)
posthog.reloadFeatureFlags();

デバイスにローカルサーバーを公開する

ngrok http 3000

無料プランではngrokが自動的にドメインを割り当てます(名前は選べません)。端末でhttps://…ngrok-free.appを開きます。最初のアクセス時にインタースティシャルが表示され、承認すると7日間有効なクッキーが設定されます。

一部の開発サーバーは未認識のHostヘッダーを拒否します。Viteはserver.allowedHosts、webpack-dev-serverはallowedHostsを持ちます。localhostからのページはトンネル経由で読み込めない場合、まずこれを確認してください。

実用的な制限

  • CloudflareのQuickトンネルは、SSEを使うアプリには適しません。
  • ホットモジュールリロードはWebSocketを使います。ページが読み込めてもライブアップデートしない場合、トンネルや開発サーバーのホスト設定を確認してください。
  • 実ユーザーデータや本番資格情報を公開トンネルに載せないでください。

3. マイクロフロントエンド: デプロイ済みシェル内のリモート上書き

Module Federationは、独立して構築されたアプリケーション間でコードをランタイム共有する仕組みです。Webpack 5には元のプラグインが含まれ、Module Federation 2.0はバンドラー非依存のランタイムやリモートの型ヒント、@module-federation/enhancedによるランタイムプラグインを追加します。これによりregisterRemotesやloadRemoteが動的にリモートを追加可能です。

一般的な開発の課題は、リモートは単体では動作しますが、スタイルの衝突や依存関係の不一致、認証に依存した動作など、シェル内に組み込むと問題が顕在化することです。解決策の一つは、シェルをデプロイしたままにしておき、リモートだけをローカルでトンネル経由で動かし、シェルのリクエストをリモートのエントリーファイルにリダイレクトすることです。

なぜlocalhostではなくトンネルなのか

  • Chromeは、パブリックサイトからローカルネットワークやループバックアドレスへのリクエストを許可のプロンプト(Local Network Access)で制限し始め、Chrome 142以降はWebSocketも対象に拡大しました。トンネルはリモートに公開HTTPSオリジンを提供し、HTTPSシェルがパブリックなループバックリクエストを行わないようにします。
  • HTTPSシェルがHTTPコンテンツを読み込むと混在コンテンツの問題が発生します。HTTPSトンネルURLを使えばこれを回避できます。
  • チームメンバーも同じオーバーライドをネットワークにいなくてもロード可能です。

ステップ1: リモートの設定

// webpack.config.js (リモート)
const { ModuleFederationPlugin } = require('@module-federation/enhanced/webpack');

module.exports = {
  mode: 'development',
  output: { publicPath: 'auto' }, // chunksはremoteEntry.jsの読み込み元に相対
  devServer: {
    port: 8081,
    allowedHosts: ['.ngrok-free.app'],  // トンネルのHostヘッダーを許可
    headers: {
      // テスト対象のシェルに限定
      'Access-Control-Allow-Origin': 'https://app.example.com',
    },
  },
  module: {
    rules: [
      // スコープ付きCSSを文字列としてインポートし、シャドウルートに注入
      { test: /\.scoped\.css$/, type: 'asset/source' },
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'checkoutMFE',
      filename: 'remoteEntry.js',
      exposes: { './CheckoutWidget': './src/CheckoutWidget.tsx' },
      shared: {
        react: { singleton: true, requiredVersion: '^18.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
      },
    }),
  ],
};

ステップ2: Shadow DOMによるスタイルの隔離とその制約

Shadow DOMはホストページのセレクタがコンポーネントに届かないようにし、逆も同様です。ただし、完全な隔離ではありません:

  • 継承されるプロパティは境界を越える。 colorやfont-family、line-heightなどは親要素から継承され、Shadow DOM内にも適用されます。
  • CSSカスタムプロパティも越境します。 テーマ設定に便利ですが、ホストの--primaryがコンポーネントに影響します。
  • all: initialを:hostに設定すると継承とカスタムプロパティがリセットされます。必要に応じて明示的にリセットしてください。
// src/CheckoutWidget.scoped.css
:host {
  all: initial;        /* ホストページからの継承を停止 */
  display: block;
}
.checkout-container { font-family: system-ui, sans-serif; }

Shadow DOMによりCSSが封じ込められるため、ホスト側のスタイル衝突は見えなくなります。ただし、継承やカスタムプロパティの前提は実際のシェルで検証してください。

ステップ3: シェルのリクエストをトンネルにリダイレクト

リモートとトンネルを起動します:

npm start            # リモートは:8081
ngrok http 8081      # https://your-name.ngrok-free.app URLを確認

次に、declarativeNetRequestを使ったChrome拡張を作成し、リクエストをブロック・変更します。これにはdeclarativeNetRequestの権限と、関係するオリジンのhost_permissionsが必要です。

manifest.json

{
  "manifest_version": 3,
  "name": "MFE Local Override",
  "version": "0.1.0",
  "permissions": ["declarativeNetRequest"],
  "host_permissions": ["https://app.example.com/*", "https://*.ngrok-free.app/*"],
  "declarative_net_request": {
    "rule_resources": [
      { "id": "mfe_override", "enabled": true, "path": "rules.json" }
    ]
  }
}

rules.json

[
  {
    "id": 1,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": { "url": "https://your-name.ngrok-free.app/remoteEntry.js" }
    },
    "condition": {
      "urlFilter": "||app.example.com/assets/mfe/checkout/remoteEntry.js",
      "resourceTypes": ["script"]
    }
  },
  {
    "id": 2,
    "priority": 1,
    "action": {
      "type": "modifyHeaders",
      "requestHeaders": [
        { "header": "ngrok-skip-browser-warning", "operation": "set", "value": "1" }
      ]
    },
    "condition": {
      "requestDomains": ["your-name.ngrok-free.app"],
      "resourceTypes": ["script", "xmlhttprequest"]
    }
  }
]

リクエストヘッダーの設定により、インタースティシャルページの警告を回避します。fetchを使った他のファイルも対象にしたい場合はresourceTypesにxmlhttprequestを追加します。

これらの静的ルールはインストール時に固定されます。トンネルのホスト名が変わる場合は動的ルールを使います。これによりブラウザのセッションや拡張機能のアップグレード後も持続します。

このパターンの安全性について

  • このオーバーライドは拡張機能をインストールしたブラウザだけに作用します。共有や本番環境に向けて設定しないでください。
  • リモートはURLを知っている誰でもアクセス可能です。スクリプトのサブリソースは認証ページがなく、IP制限(ngrokのrestrict-ips)の方が適しています。
  • シェルとリモートがsingleton依存関係を共有している場合、バージョン不一致(例:Reactのメジャーバージョン違い)はランタイムエラーとして現れます。共有設定を修正してください。

まとめ

目的 トンネルが役立つ場面 役立たない場面 主要ツール
ビジュアルリグレッション リモートブラウザ、デバイスクラウド、レビュアーがローカルビルドを読み込む必要がある localhostに対して既にPlaywrightやPercy、Chromaticが動作 Playwright toHaveScreenshot(), @ngrok/ngrok, Percy, Chromatic
デバイス上のフラグテスト 実機がローカルアプリを読み込む必要がある フラグ値はデバイスとベンダークラウド間を流れる LaunchDarkly, PostHog, ngrok
マイクロフロントエンドの上書き HTTPSデプロイ済みシェルが公開HTTPSリモートを必要とする Shadow DOMは継承やカスタムプロパティの越境を防止しない Module Federation 2.0, declarativeNetRequest, Shadow DOM

トンネルワークフローのセキュリティチェックリスト

  1. すべてのトンネルに認証または制限を設定。ngrokはBasic AuthやOAuth、IP制限をサポートし、@ngrok/ngrokはbasic_authやtraffic_policyを受け入れます。Cloudflare Quick Tunnelsはデフォルトでは認証なしですが、cloudflaredの--allowed-mailオプションでメール認証を設定可能です。
  2. トークンはソース管理に含めない。NGROK_AUTHTOKENやCloudflareの資格情報は環境変数や秘密管理ツールから読み込み、定期的にローテーションしてください。
  3. 短命のトンネルを使用。finallyブロックやシグナルハンドラでリスナーを閉じ、クラッシュしたスクリプトが開発サーバーを露出し続けないようにします。
  4. 本番データや資格情報を公開トンネルに載せない。フィクスチャや開発環境を利用してください。
  5. 無料プランの制限を事前に確認。インタースティシャル、ドメイン制限、リクエスト制限、SSE非サポートなどの制約があります。

結論

リモート側があなたのローカルビルドにアクセスする必要がある場合にトンネルは有効です。実機、ブラウザホスティング、デプロイ済みシェル、レビュアー向けです。すべてが一台のマシン上で動作している場合や、フラグSDKやスクリーンショットサービスの通信には不要です。問題に合わせてパターンを選び、トンネルの制限を確認し、エンドポイントを適切に保護してください。

参考資料

Continue from this article into the most relevant product guides and workflows.

Related Topics

#developer experience#frontend architecture#localhost tunnels#visual regression testing#Percy testing#Playwright visual regression#Chromatic snapshot testing#local tunnel CI testing#staging environment alternatives#client-side feature flags#LaunchDarkly local testing#PostHog feature flags#tunneling proxies#multi-tenant testing#physical test devices#CSS leakage isolation#browser extension injectors#multi-origin component testing#micro-frontend architecture#federated modules#local module federation#tunnel endpoints#host frame testing#modern frontend workflows#developer productivity tools#frontend testing pipelines#automated visual testing#local development proxy#remote tunneling tools#web development best practices#scalable frontend architecture#continuous integration testing#UI regression testing#frontend observability#local environment replication#secure localhost tunnels#dynamic feature flagging#multi-tenant state testing#cross-origin testing tools#micro-frontend integration#distributed frontend testing#software engineering best practices#devops for frontend#agile web development#modern web tooling#frontend code quality#automated UI testing#component isolation techniques#web app testing strategies

Keep building with InstaTunnel

Read the docs for implementation details or compare plans before you ship.

Share this article

More InstaTunnel Insights

Discover more tutorials, tips, and updates to help you build better with localhost tunneling.

Browse All Articles