Tutorial
30 min read
35 views

Modern Developer Workflows & Advanced Infrastructure Integrations

標準のHTTPプロキシがNext.js 15+ Server ActionsやVite WebSocket HMR接続を切断する理由と、リモートトンネル上でサブミリ秒のホットリロードを維持する方法を解説します。

IT
InstaTunnel Team
Published by the InstaTunnel team | Editorial policy
Modern Developer Workflows & Advanced Infrastructure Integrations

Quick answer

Vite 6 & Next.js Server Actionsのトンネリング:HMRの修正: webhook testing answer

For local webhook testing, run your app locally, expose it with a public HTTPS tunnel, and paste the stable callback URL into the provider dashboard.

How do I test webhooks on localhost?

Start your local server, open a public HTTPS tunnel to that port, configure the provider webhook URL, and inspect events in your local logs.

Why does a stable webhook URL matter?

Stable URLs prevent provider dashboards from needing manual callback updates every time you restart a tunnel.

リモートクラウドランタイムと開発マシン上の瞬時のフィードバックループをバランスさせるのが現代のフルスタックアーキテクチャです。Next.jsフロントエンドや分散マイクロサービスの高速なイテレーションを実現するには、クラウド境界とローカル開発環境間でライブトラフィックをルーティングする必要があります。

このガイドでは、ローカル開発サーバーのプロキシ化、テレメトリスパンの転送、多テナントWebhooksのルーティング、一時的なデータベースインスタンスへのクラウドプレビューのアタッチなどの技術戦略を紹介します。

1. ボーダーを越えたホットリロード:Vite 6とNext.js Server ActionsをHMR対応プロキシ経由でトンネリング

従来のHTTPリバースプロキシ(例:Nginxや基本的なSSHトンネル)は、ステートレスにトラフィックを評価するため、状態を持つWebアプリケーションを破壊します。現代の開発ツールであるVite 6やNext.js 15+は、クライアント状態の同期やサーバーサイドモジュールのコンパイル、Server Actionsの処理に双方向・低遅延の接続を必要とします。

+-------------------------------------------------------------------------------+
|                             パブリッククラウドトンネル                          |
|                     https://dev-tenant.app.example.com                        |
+-------------------------------------------------------------------------------+
                                       |
                                       | TLSターミナル&WSSアップグレード
                                       v
+-------------------------------------------------------------------------------+
|                       HMR対応リバーストンネルプロキシ                          |
|  - Hostヘッダー書き換え(オリジン検証)                                       |
|  - HTTP/1.1アップグレード処理(101 Switching Protocols)                     |
|  - Keep-Alive Pingsと動的クライアントポートマッピング                            |
+-------------------------------------------------------------------------------+
                                       |
            +--------------------------+--------------------------+
            | (gRPC / 多重TCP)                                | (HTTP/2 Server Actions)
            v                                                     v
+-----------------------+                             +-----------------------+
|  Vite Dev Server      |                             | Next.js App Router    |
|  (localhost:5173)      |                             | (localhost:3000)      |
|                       |                             |                       |
| - HMR WebSocket Path: |                             | - CSRF許可ホスト:     |
|   /_vite_hmr          |                             |   dev-tenant.example  |
+-----------------------+                             +-----------------------+

従来のプロキシがHMRとServer Actionsを破壊する理由

  1. WebSocketフレーミングの喪失: 標準のHTTPプロキシは、リクエスト/レスポンスの短命なペアとしてトラフィックを扱います。ViteがHTTP/1.1のUpgrade: websocketを使ってHMRチャネルを確立しようとすると、ステートレスなプロキシは持続的なTCPソケットを維持できず、結果的に継続的なリロードに追い込まれます。
  2. Next.js 15+のHostヘッダーとCSRFチェック: Next.js Server Actionsは、HTTP POSTリクエストと隠しエンドポイントIDを使って実行されます。CSRF攻撃を防ぐため、Next.jsはOriginとHostヘッダーを検査します。トンネルがHost: localhost:3000を書き換え、公開トンネルURLと一致しない場合、Next.jsは呼び出しをブロックします。
  3. WebSocketポートの不一致: Viteはクライアント側のスクリプトをブラウザに注入し、HMRポートへのWebSocket接続を試みます。ブラウザがhttps://app.example.com(ポート443)に接続しているのに対し、Viteがws://localhost:5173に接続を指示すると、クロスオリジンのセキュリティポリシーにより接続がブロックされます。

Vite 6のための保存アーキテクチャ

パブリックトンネル(Cloudflare Tunnels、frp、ngrokなど)上でサブミリ秒のHMRを維持するには、Viteの内部WebSocketサーバーのリスニングポートとクライアント向けの公開ポートを分離設定します。

// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    host: '0.0.0.0',
    port: 5173,
    strictPort: true,
    hmr: {
      // トンネルが公開するホスト名
      host: 'vite-tunnel.dev.example.com',
      // クライアントがHTTPS/WSSポートを使用するよう強制
      clientPort: 443,
      protocol: 'wss',
      path: '/_vite_hmr',
    },
    // ホストの不一致によるHMR切断を防止
    cors: true,
  },
});

Next.js 15+ Server Actionトンネル設定

Next.js 15では、next.config.tsを更新して、パブリック開発トンネル経由でServer Actionsの呼び出しを許可します。

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  experimental: {
    serverActions: {
      // トンネルのオリジンをホワイトリストに登録
      allowedOrigins: [
        'next-tunnel.dev.example.com',
        '*.tunnel.dev.example.com',
      ],
    },
  },
};

export default nextConfig;

2. クラウドステージングからローカルJaeger UIへの分散トレースルーティング

ステージング環境でマイクロサービスのトレースをデバッグする際、非同期リクエストの追跡は重要です。ただし、テレメトリデータを中央のストレージに送るとノイズが増え、隔離が難しくなります。

OpenTelemetry(OTLP)コレクターを使えば、リモートストレージを汚染せずに、ローカルのJaeger UIにテレメトリストリームをプロキシできます。

+-----------------------------------------------------------------------------------+
|                              クラウドステージングクラスタ                        |
|                                                                                   |
|  +---------------------+      +---------------------+      +-------------------+  |
|  |  認証マイクロサービス  | --- | 受注マイクロサービス  | --- | 決済サービス       |  |
|  +---------------------+      +---------------------+      +-------------------+  |
|                                          |                                        |
|                                          | OTLP / gRPC (ポート 4317)               |
|                                          v                                        |
|                      +---------------------------------------+                    |
|                      |   クラウドステージングOTLPコレクター   |                    |
|                      +---------------------------------------+                    |
+------------------------------------------|----------------------------------------+
                                           |
                                           | Tailscale/SSHリバーストンネル経由でルーティング
                                           v
+-----------------------------------------------------------------------------------+
|                             ローカル開発者ワークステーション                     |
|                                                                                   |
|  +-----------------------------------------------------------------------------+  |
|  | ローカルOpenTelemetryコレクター                                              |  |
|  | - 受信: OTLP / gRPC on 127.0.0.1:4317                                         |  |
|  | - フィルタ: attributes["x-developer-id"] == "dev-alice"                     |  |
|  +-----------------------------------------------------------------------------+  |
|                                          |                                        |
|                                          v OTLP / gRPC (非暗号化)                |
|  +-----------------------------------------------------------------------------+  |
|  | ローカルJaeger UIコンテナ                                                    |  |
|  | - 受信ポート: 4317                                                          |  |
|  | - Web UI: http://localhost:16686                                              |  |
|  +-----------------------------------------------------------------------------+  |
+-----------------------------------------------------------------------------------+

トレースハイジャッキング問題

クラウドストア(Datadog、Honeycomb、AWS OpenSearchなど)に直接トレーススパンを送ると、次の2つの問題が発生します:

  • インデックス汚染: デバッグログや不正なペイロードが本番・ステージングダッシュボードを汚染します。
  • レイテンシとレート制限: 高ボリュームのスパンをインターネット越しに送信すると、クラウドの取り込みコスト増や、ベンダーのレート制限超過リスクが高まります。

アーキテクチャ:選択的OTLPリバースルーティング

コンテキストヘッダー(例:x-developer-id: dev-alice)を使い、OpenTelemetryコレクターのルーティングコネクタでタグ付けされたスパンだけをローカルに逆ルーティングできます。

ステップ1: ステージングOTLPコレクターの設定

ステージングのOTLPコレクターに、受信したトレース属性を評価し、タグ付けされたスパンを逆gRPCパイプラインでローカルマシンにルーティングさせる設定をします。

# otel-collector-staging.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317

processors:
  batch:
    timeout: 1s
    send_batch_size: 256

exporters:
  # 標準のクラウドストレージ宛
  otlp/staging_backend:
    endpoint: "tempo-staging.internal.net:4317"
    tls:
      insecure: true

  # 逆トンネル先(開発者マシン)
  otlp/dev_alice_local:
    endpoint: "host.docker.internal:14317"
    tls:
      insecure: true

connector:
  routing:
    default_exporters: [otlp/staging_backend]
    from_attribute: "x-developer-id"
    table:
      - value: "dev-alice"
        exporters: [otlp/dev_alice_local]

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [routing]

ステップ2: リバーストンネルとローカル受信の確立

開発者のワークステーションからステージングゲートウェイへ逆SSHトンネルを確立し、ポート14317をローカルの4317にマッピングします。

# 逆SSHgRPCトンネルの確立
ssh -R 14317:localhost:4317 developer@staging-gateway.internal.net -N

次に、軽量なローカルJaegerコンテナを起動します(OTLP受信設定済み):

docker run -d --name jaeger-local \
  -e COLLECTOR_OTLP_ENABLED=true \
  -p 16686:16686 \
  -p 4317:4317 \
  jaegertracing/all-in-one:latest

HTTPリクエストにx-developer-id: dev-aliceヘッダーが付いていると、ステージング環境に入り、そのトレースツリー全体が分岐し、http://localhost:16686のローカルJaeger UIにストリーミングされます。

3. ローカルStripe Connect Webhookの動的マルチテナントサブドメインテスト

マルチテナントSaaSプラットフォームのテストには、異なる組織のイベントを隔離する必要があります。Stripe Connectを使ったマルチテナントアーキテクチャ(プラットフォームが複数の接続済みアカウントを管理)では、Webhookのデバッグが動的テナント解決問題を隠すことがあります。

                                  STRIPE CLOUD
                                        |
                 +----------------------+----------------------+
                 | Webhookイベント                          |
                 | アカウント: acct_tenant_A                  | アカウント: acct_tenant_B
                 v                                             v
  +------------------------------+             +------------------------------+
  | https://tenant-a.tunnel.dev  |             | https://tenant-b.tunnel.dev  |
  +------------------------------+             +------------------------------+
                 |                                             |
                 +----------------------+----------------------+
                                        |
                                        v
                        +-------------------------------+
                        | ワイルドカードIngressプロキシ |
                        | *.tunnel.dev -> localhost:8080 |
                        +-------------------------------+
                                        |
                                        v
                        +-------------------------------+
                        | ローカルマルチテナントルーター   |
                        | Hostヘッダー抽出                   |
                        | テナントAとBのマッピング             |
                        +-------------------------------+

マルチテナントWebhookルーティング設定

frpやcaddyのようなワイルドカードリバースプロキシを使い、*.tunnel.yourdomain.devをローカルアプリケーションルーターに向けます。

1. Caddyリバースプロキシ設定(ローカルIngressルーター)

# Caddyfile - マルチテナントワイルドカードローカルプロキシ
*.tunnel.dev.localhost {
    tls internal
    
    @tenant_a header_regexp host Host ^([a-zA-Z0-9-]+)\.tunnel\.dev\.localhost$
    
    handle {
        reverse_proxy localhost:3000 {
            header_up Host {http.request.host}
            header_up X-Forwarded-Host {http.request.host}
        }
    }
}

2. 高度なマルチテナントWebhookフォワーダースクリプト

個別のstripe listenコマンドをテナントごとに実行する代わりに、このNode.jsのマルチテナントCLIプロキシスクリプトを使います。Stripeイベントを受信し、account IDに基づいて動的にローカルサブドメインに転送します。

// scripts/stripe-multitenant-proxy.ts
import Stripe from 'stripe';
import axios from 'axios';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2026-01-28',
});

// Stripe接続アカウントIDとローカルサブドメインのマッピング
const TENANT_MAPPING: Record<string, string> = {
  'acct_1N01AAA000000000': 'acme',
  'acct_1N02BBB000000000': 'globex',
};

async function handleWebhookEvent(event: Stripe.Event) {
  const connectedAccountId = event.account;
  const tenantSlug = connectedAccountId ? TENANT_MAPPING[connectedAccountId] : 'app';

  if (!tenantSlug) {
    console.warn(`[WARN] 未登録のアカウントID: ${connectedAccountId}`);
    return;
  }

  const targetUrl = `http://${tenantSlug}.tunnel.dev.localhost:3000/api/webhooks/stripe`;

  try {
    console.log(`[FORWARD] イベント ${event.type} -> ${targetUrl}`);
    await axios.post(targetUrl, event, {
      headers: {
        'Stripe-Signature': 'simulated_local_sig',
        'Content-Type': 'application/json',
        'Host': `${tenantSlug}.tunnel.dev.localhost`,
      },
    });
  } catch (err: any) {
    console.error(`[ERROR] Webhook送信失敗: ${err.message}`);
  }
}

4. ローカルSupabaseとPostgresインスタンスをクラウドVercel/Netlifyプレビューに公開

ブランチごとのワークフローでは、一時的なフロントエンドプレビューがVercelやNetlify上に自動的に立ち上がります。ただし、各PRプレビューに対して隔離されたバックエンドデータベース状態を提供するのは難しいです。

ngrok tcpやbore、frpのようなLayer 4 TCPリバーストンネルを使えば、ローカルのPostgreSQLやDocker化したSupabaseインスタンスを安全にクラウドプレビューに公開できます。

+-----------------------------------------------------------------------------+
|                        VERCEL / NETLIFY プレビュー PR #42                     |
|                     https://app-git-feature-pr42.vercel.app                |
|                                                                             |
|   DATABASE_URL = postgresql://postgres:pass@tcp.tunnel.dev:19432/postgres  |
+-----------------------------------------------------------------------------+
                                       |
                                       | 暗号化されたTCP(ポート19432)
                                       v
+-----------------------------------------------------------------------------+
|                            TCPリバースプロキシ                                |
|  - TLS透過 / L4 TCPパイプライン                                              |
+-----------------------------------------------------------------------------+
                                       |
                                       | セキュアリバーストンネル
                                       v
+-----------------------------------------------------------------------------+
|                        ローカル開発者ワークステーション                        |
|                                                                             |
|  +-----------------------------------------------------------------------+  |
|  | ローカルPostgresファイアウォール / PgBouncerプロキシ                     |  |
|  | - 127.0.0.1:5432で待ち受け                                              |  |
|  | - SSL制限 / セッション制限の検証                                          |  |
|  +-----------------------------------------------------------------------+  |
|                                      |                                      |
|                                      v                                      |
|  +-----------------------------------------------------------------------+  |
|  | Docker化Supabaseインスタンス                                              |  |
|  | - PostgreSQLエンジン(ポート5432)                                         |  |
|  | - PostgREST API(ポート54321)                                              |  |
|  | - GoTrue認証システム(ポート9999)                                           |  |
|  +-----------------------------------------------------------------------+  |
+-----------------------------------------------------------------------------+

プロトコル比較:Layer 4(TCP) vs Layer 7(HTTP)

  • HTTPプロキシ(Layer 7): データベースエンジンには不適。プレーンなHTTP/WebSocketを期待し、PostgresのStartupMessageなどのワイヤプロトコルを破壊します。
  • TCPトンネル(Layer 4): 生のTCPパケットをトランスポート層で転送し、ワイヤプロトコルを維持します。

SupabaseローカルスタックをFRP(高速リバースプロキシ)で公開

ローカルのSupabaseスタックをトンネルするには、frpを設定してPostgreSQL(ポート5432)とPostgREST(ポート54321)をL4 TCP経由で公開します。

サーバ設定(frps.ini onリモートゲートウェイ)

[common]
bind_port = 7000
vhost_extra_db_port = 19432

クライアント設定(frpc.ini on開発者ワークステーション)

[common]
server_addr = gateway.yourdomain.dev
server_port = 7000

[supabase-postgres-tcp]
type = tcp
local_ip = 127.0.0.1
local_port = 54322
remote_port = 19432

[supabase-postgrest-http]
type = http
local_ip = 127.0.0.1
local_port = 54321
custom_domains = api-pr42.tunnel.yourdomain.dev

GitHub ActionsとVercel APIによる自動化

一時的なプレビュー展開に対して、GitHub Actionsを使い動的に環境変数を設定し、データベース接続情報を自動挿入します。

# .github/workflows/preview-environment.yml
name: Preview Environment Database Injector

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  attach-local-db:
    runs-on: ubuntu-latest
    steps:
      - name: Vercelプレビューに動的DBトンネルURLを注入
        env:
          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
        run: |
          PR_NUM=${{ github.event.number }}
          # 開発者のTCPトンネルポートを指す動的DB URLを構築
          TUNNEL_DB_URL="postgresql://postgres:postgres@gateway.yourdomain.dev:19432/postgres?sslmode=require"

          # Vercelプレビュー環境にデータベース接続文字列を設定
          curl -X POST "https://api.vercel.com/v10/projects/${VERCEL_PROJECT_ID}/env" \
            -H "Authorization: Bearer ${VERCEL_TOKEN}" \
            -H "Content-Type: application/json" \
            -d '{
              "key": "DATABASE_URL",
              "value": "'"${TUNNEL_DB_URL}"'",
              "type": "encrypted",
              "target": ["preview"],
              "gitBranch": "${{ github.head_ref }}"
            }'

アーキテクチャ概要:リバースプロキシと連携パターン

パターン プロトコル層 用途 注意点・落とし穴
HMRプロキシ Layer 7 (HTTP/WSS) Vite 6 / Next.js 15+のリモートトンネル越しのホットリロード Next.jsのallowedOriginsホワイトリストとViteのclientPort上書きが必要
OTLPトレースルーティング Layer 7 (gRPC / HTTP/2) OpenTelemetryを使ったマイクロサービスのトレース隔離 ヘッダー伝播ミドルウェア(x-developer-id)の導入が必要
動的サブドメイン Layer 7 (HTTP/SNI) マルチテナントSaaS Webhook&チェックアウトルートシミュレーション ワイルドカードSSL証明書(*.domain.dev)またはローカルCA証明書の導入が必要
L4データベース逆トンネル Layer 4 (純TCP) Vercel/NetlifyのクラウドプレビューとローカルDBの連携 高い接続数はローカルDBプールを枯渇させるため、PgBouncerの利用推奨

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

Related Topics

#vite 6 hmr#nextjs server actions#websocket proxy#hmr over proxy#hot module replacement#vite tunnel#nextjs 15 tunneling#localhost tunnel#reverse proxy hmr#websocket drops proxy#nextjs server actions debug#local developer environment#vite dev server proxy#hmr websocket reconnect#server action proxy#tunnel localhost#local dev tunneling#remote hmr debugging#web sockets over reverse proxy#sub millisecond hmr#vite HMR configuration#nextjs server actions deployment#developer workflow optimization#modern frontend frameworks#local dev environment tools#web development proxies#tunneling tools for developers#nextjs preview local database#remote webhook debugging#local dev setup#web development workflow#fullstack local debugging#software development tools#devops local environment#local proxy configuration#developer networking tools#frontend developer environment#backend developer environment#fullstack developer tools#real time hot reload#continuous development workflow#local dev server port forwarding#local port tunnel#secure localhost exposure#web development productivity#server action execution#websocket connection handling#developer tooling 2026#remote development server#web dev tunneling solutions

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