DEV Community

Cover image for Modern Developer Workflows & Advanced Infrastructure Integrations
InstaTunnel
InstaTunnel

Posted on Originally published at instatunnel.my Fully Autonomous

Modern Developer Workflows & Advanced Infrastructure Integrations

Modern full-stack architecture balances remote cloud runtimes with instantaneous feedback loops on developer machines. Achieving rapid iteration—whether in Next.js frontend applications or distributed microservices—requires routing live traffic between cloud boundaries and local developer environments.

This guide provides technical strategies for proxying local dev servers, forwarding telemetry spans, routing multi-tenant webhooks, and attaching cloud previews to ephemeral database instances.

1. Hot-Reload Across Borders: Tunneling Vite 6 and Next.js Server Actions over HMR-Aware Proxies

Traditional HTTP reverse proxies (like default Nginx or basic SSH tunnels) break stateful web applications because they evaluate traffic statelessly. Modern development tools like Vite 6 and Next.js 15+ rely on bidirectional, low-latency connections to sync client states, compile server-side modules, and process Server Actions.

+-------------------------------------------------------------------------------+
|                             PUBLIC CLOUD TUNNEL                               |
|                     https://dev-tenant.app.example.com                        |
+-------------------------------------------------------------------------------+
                                       |
                                       | TLS Terminal & WSS Upgrade
                                       v
+-------------------------------------------------------------------------------+
|                       HMR-AWARE REVERSE TUNNEL PROXY                          |
|  - Host Header Rewriting (Origin Verification)                                |
|  - HTTP/1.1 Upgrade Handling (101 Switching Protocols)                        |
|  - Keep-Alive Pings & Dynamic Client Port Mapping                             |
+-------------------------------------------------------------------------------+
                                       |
            +--------------------------+--------------------------+
            | (gRPC / Multiplexed TCP)                            | (HTTP/2 Server Actions)
            v                                                     v
+-----------------------+                             +-----------------------+
|  Vite Dev Server      |                             | Next.js App Router    |
|  (Localhost:5173)     |                             | (Localhost:3000)      |
|                       |                             |                       |
| - HMR WebSocket Path: |                             | - CSRF Allowed Host:  |
|   /_vite_hmr          |                             |   dev-tenant.example  |
+-----------------------+                             +-----------------------+
Enter fullscreen mode Exit fullscreen mode

Why Traditional Proxies Break HMR and Server Actions

  1. WebSocket Framing Dropped: Standard HTTP proxies treat incoming requests as short-lived request/response pairs. When Vite attempts an HTTP/1.1 Upgrade: websocket exchange to establish its Hot Module Replacement (HMR) channel, stateless proxies fail to maintain the persistent TCP socket. This forces Vite into continuous fallback reloads.
  2. Next.js 15+ Host Header & CSRF Checks: Next.js Server Actions execute via HTTP POST requests using hidden endpoint IDs. To prevent Cross-Site Request Forgery (CSRF) attacks, Next.js inspects the incoming Origin and Host headers. If a tunnel rewrites Host: localhost:3000 without matching the public tunnel URL, Next.js blocks the invocation.
  3. Mismatched WebSocket Ports: Vite injects a client-side runtime script into the browser that attempts to open a WebSocket connection back to the HMR port. If the browser connects to [https://app.example.com](https://app.example.com) (port 443) but Vite tells the client to open a socket to ws://localhost:5173, cross-origin browser security blocks the connection.

Preservation Architecture for Vite 6

To maintain sub-millisecond HMR over public tunnels (such as Cloudflare Tunnels, frp, or ngrok), configure Vite's internal WebSocket server to decouple its internal listening port from its public client-facing port.

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

export default defineConfig({
  server: {
    host: '0.0.0.0',
    port: 5173,
    strictPort: true,
    hmr: {
      // The public hostname exposed by your tunnel
      host: 'vite-tunnel.dev.example.com',
      // Force client to use HTTPS/WSS port instead of internal 5173
      clientPort: 443,
      protocol: 'wss',
      path: '/_vite_hmr',
    },
    // Prevent HMR connection dropouts due to host mismatch
    cors: true,
  },
});
Enter fullscreen mode Exit fullscreen mode

Next.js 15+ Server Action Tunnel Configuration

For Next.js 15, update next.config.ts to allow invocation of Server Actions through public dev tunnels.

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

const nextConfig: NextConfig = {
  experimental: {
    serverActions: {
      // Whitelist tunnel origins to pass CSRF origin checks
      allowedOrigins: [
        'next-tunnel.dev.example.com',
        '*.tunnel.dev.example.com',
      ],
    },
  },
};

export default nextConfig;
Enter fullscreen mode Exit fullscreen mode

2. Routing Distributed Traces from Cloud Staging to Your Local Jaeger UI

When debugging microservices in staging environments, tracing an asynchronous request through multiple services is vital. However, sending telemetry data to a central telemetry storage backend creates noise and makes isolation difficult.

Using OpenTelemetry (OTLP) collectors, backend engineers can proxy telemetry streams down to a local Jaeger UI instance without polluting remote storage.

+-----------------------------------------------------------------------------------+
|                              CLOUD STAGING CLUSTER                                |
|                                                                                   |
|  +---------------------+      +---------------------+      +-------------------+  |
|  |  Auth Microservice  | ---> | Order Microservice  | ---> | Payment Service   |  |
|  +---------------------+      +---------------------+      +-------------------+  |
|                                          |                                        |
|                                          | OTLP / gRPC (Port 4317)                |
|                                          v                                        |
|                      +---------------------------------------+                    |
|                      |   Cloud Staging OTLP Collector     |                    |
|                      +---------------------------------------+                    |
+------------------------------------------|----------------------------------------+
                                           |
                                           | Routing via Tailscale/SSH Reverse Tunnel
                                           v
+-----------------------------------------------------------------------------------+
|                             LOCAL DEVELOPER WORKSTATION                           |
|                                                                                   |
|  +-----------------------------------------------------------------------------+  |
|  | Local OpenTelemetry Collector                                               |  |
|  | - Receives: OTLP / gRPC on 127.0.0.1:4317                                    |  |
|  | - Filter: attributes["x-developer-id"] == "dev-alice"                       |  |
|  +-----------------------------------------------------------------------------+  |
|                                          |                                        |
|                                          v OTLP / gRPC (Insecure)                 |
|  +-----------------------------------------------------------------------------+  |
|  | Local Jaeger UI Container                                                   |  |
|  | - Ingestion Port: 4317                                                      |  |
|  | - Web UI: http://localhost:16686                                          |  |
|  +-----------------------------------------------------------------------------+  |
+-----------------------------------------------------------------------------------+
Enter fullscreen mode Exit fullscreen mode

The Trace Hijacking Problem

Sending trace spans directly to cloud stores like Datadog, Honeycomb, or a shared AWS OpenSearch instance presents two distinct issues:

  • Index Contamination: Debugging logs and malformed payloads clutter production and staging dashboards.
  • Latency & Rate Limits: Exporting high-volume spans over internet bounds increases cloud ingestion costs and risks exceeding vendor rate limits.

Architecture: Selective OTLP Reverse Routing

Engineers can selectively isolate and forward traces using contextual headers (such as x-developer-id: dev-alice) processed by an OpenTelemetry Collector routing connector.

Step 1: Staging OpenTelemetry Collector Configuration

Configure the staging OTLP collector to evaluate incoming trace attributes and route tagged spans through a reverse gRPC pipeline back to a local machine.

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

processors:
  batch:
    timeout: 1s
    send_batch_size: 256

exporters:
  # Standard cloud storage destination
  otlp/staging_backend:
    endpoint: "tempo-staging.internal.net:4317"
    tls:
      insecure: true

  # Reverse tunnel destination pointing to the developer machine
  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]
Enter fullscreen mode Exit fullscreen mode

Step 2: Establish the Reverse Tunnel and Local Ingestion

Establish a reverse SSH tunnel from your developer workstation to the staging gateway, mapping port 14317 on the staging node to port 4317 locally.

# Establish reverse SSH gRPC tunnel
ssh -R 14317:localhost:4317 developer@staging-gateway.internal.net -N
Enter fullscreen mode Exit fullscreen mode

Run a lightweight local Jaeger container (configured with native OTLP ingestion):

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

When an HTTP request containing the header x-developer-id: dev-alice enters the staging environment, its entire trace tree branches off and streams directly to your local Jaeger UI at http://localhost:16686.

3. Testing Local Stripe Connect Webhooks with Dynamic Multi-Tenant Subdomains

Testing multi-tenant SaaS platforms requires isolating events for distinct organizations. When building multi-tenant architectures using Stripe Connect (where a platform manages multiple connected accounts), debugging webhooks through a single endpoint hides dynamic tenant resolution issues.

                                  STRIPE CLOUD
                                        |
                 +----------------------+----------------------+
                 | Connect Webhook Event                       |
                 | Account: acct_tenant_A                      | Account: acct_tenant_B
                 v                                             v
  +------------------------------+             +------------------------------+
  | https://tenant-a.tunnel.dev  |             | https://tenant-b.tunnel.dev  |
  +------------------------------+             +------------------------------+
                 |                                             |
                 +----------------------+----------------------+
                                        |
                                        v
                        +-------------------------------+
                        | WILDCARD INGRESS PROXY        |
                        | *.tunnel.dev -> localhost:8080|
                        +-------------------------------+
                                        |
                                        v
                        +-------------------------------+
                        | LOCAL MULTI-TENANT ROUTER     |
                        | Extracts Host header          |
                        | Maps Tenant A vs Tenant B     |
                        +-------------------------------+
Enter fullscreen mode Exit fullscreen mode

Multi-Tenant Webhook Routing Setup

Using a wildcard reverse proxy (such as frp or caddy), point *.tunnel.yourdomain.dev to your local application router.

1. Caddy Reverse Proxy Configuration (Local Ingress Router)

# Caddyfile - Multi-tenant Wildcard Local Proxy
*.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}
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Advanced Multi-Tenant Webhook Forwarder script

Instead of running individual stripe listen commands per tenant, use this Node.js multi-tenant CLI proxy script. It listens to Stripe events and dynamically forwards them to the corresponding local subdomain based on the event's 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',
});

// Map Stripe Connected Account IDs to Local Subdomains
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] Unmapped account ID: ${connectedAccountId}`);
    return;
  }

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

  try {
    console.log(`[FORWARD] Event ${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] Failed to deliver webhook: ${err.message}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Exposing Local Supabase and Postgres Instances to Cloud Vercel/Netlify Preview Deployments

Feature branch workflows automatically spin up ephemeral frontend preview deployments on platforms like Vercel or Netlify. However, providing isolated backend database state for every PR preview can be difficult to manage.

Using Layer 4 TCP reverse tunneling (such as ngrok tcp, bore, or frp), you can safely expose your local PostgreSQL or Dockerized Supabase instance directly to cloud preview deployments.

+-----------------------------------------------------------------------------+
|                        VERCEL / NETLIFY PREVIEW PR #42                      |
|                     https://app-git-feature-pr42.vercel.app                 |
|                                                                             |
|   DATABASE_URL = postgresql://postgres:pass@tcp.tunnel.dev:19432/postgres   |
+-----------------------------------------------------------------------------+
                                       |
                                       | Encrypted TCP (Port 19432)
                                       v
+-----------------------------------------------------------------------------+
|                            TCP REVERSE PROXY                                |
|  - Pass-through TLS / Pure L4 TCP Pipeline                                  |
+-----------------------------------------------------------------------------+
                                       |
                                       | Secure Reverse Tunnel
                                       v
+-----------------------------------------------------------------------------+
|                        LOCAL DEVELOPER WORKSTATION                          |
|                                                                             |
|  +-----------------------------------------------------------------------+  |
|  | Local Postgres Firewall / PgBouncer Proxy                             |  |
|  | - Listens on 127.0.0.1:5432                                           |  |
|  | - Restricts SSL / Validates Session Limits                            |  |
|  +-----------------------------------------------------------------------+  |
|                                      |                                      |
|                                      v                                      |
|  +-----------------------------------------------------------------------+  |
|  | Dockerized Supabase Instance                                          |  |
|  | - PostgreSQL Engine (Port 5432)                                       |  |
|  | - PostgREST API (Port 54321)                                          |  |
|  | - GoTrue Auth System (Port 9999)                                      |  |
|  +-----------------------------------------------------------------------+  |
+-----------------------------------------------------------------------------+
Enter fullscreen mode Exit fullscreen mode

Protocol Comparison: Layer 4 (TCP) vs Layer 7 (HTTP)

  • HTTP Proxies (Layer 7): Unsuitable for database engines. They expect plain HTTP/WebSockets, break wire protocols like Postgres StartupMessage, and drop non-HTTP connections.
  • TCP Tunnels (Layer 4): Forward raw TCP packets at the transport layer, keeping wire protocols intact.

Exposing Supabase Local Stack via FRP (Fast Reverse Proxy)

To tunnel your local Supabase stack, set up frp to expose local PostgreSQL (port 5432) and PostgREST (port 54321) over L4 TCP.

Server Configuration (frps.ini on Remote Gateway)

[common]
bind_port = 7000
vhost_extra_db_port = 19432
Enter fullscreen mode Exit fullscreen mode

Client Configuration (frpc.ini on Developer Workstation)

[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
Enter fullscreen mode Exit fullscreen mode

Automation via GitHub Actions & Vercel API

Automate your pull request workflows by dynamically setting environment variables on ephemeral preview deployments using a GitHub Action.

# .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: Inject Dynamic DB Tunnel URL to Vercel Preview
        env:
          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
        run: |
          PR_NUM=${{ github.event.number }}
          # Construct dynamic database URL pointing to the developer's assigned TCP tunnel port
          TUNNEL_DB_URL="postgresql://postgres:postgres@gateway.yourdomain.dev:19432/postgres?sslmode=require"

          # Assign database connection string to Vercel Preview deployment environment
          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 }}"
            }'
Enter fullscreen mode Exit fullscreen mode

Architectural Summary: Reverse Proxies & Integration Patterns

Pattern Protocol Layer Best Used For Key Edge Cases & Caveats
HMR Proxying Layer 7 (HTTP/WSS) Vite 6 / Next.js 15+ local hot reloading across remote tunnels Requires allowedOrigins whitelist in Next.js and explicitly overriding clientPort in Vite.
OTLP Trace Routing Layer 7 (gRPC / HTTP/2) Isolating microservice trace spans locally via OpenTelemetry Requires header-propagation middleware (x-developer-id) across service boundaries.
Dynamic Subdomains Layer 7 (HTTP/SNI) Multi-tenant SaaS webhook & checkout route simulation Requires wildcard SSL certificates (*.domain.dev) or local CA authority installation.
L4 Database Reverse Tunnels Layer 4 (Pure TCP) Linking Vercel/Netlify cloud preview environments to local databases High connection concurrency can exhaust local database pools; use PgBouncer locally.

Originally published at https://instatunnel.my/blog/modern-developer-workflows-advanced-infrastructure-integrations

Top comments (0)