Traversio

Remote Port Forwarding

Ask the SSH server to listen remotely, then either accept raw forwarded connections yourself or bridge them back to one local TCP service.

Remote port forwarding is the opposite of local forwarding.

Instead of making a local port on your machine point at a remote service, you make the SSH server side listen, and incoming remote connections are sent back to your local machine.

This is the classic answer to:

"My local dev server is on my laptop. How do I expose it to the remote side through SSH?"

Mental Model

remote client
    -> remote listener on the SSH server side
        -> SSH tunnel
            -> your local service

Two Public Levels In Traversio

Traversio exposes two remote-forwarding APIs:

  • withRemotePortForwardListener(...)
  • withRemotePortForwarding(...)

Use the listener API when you want raw control over each incoming forwarded-tcpip channel.

Use the bridge helper when every incoming remote connection should go to one fixed local TCP endpoint.

Example: Expose A Local HTTP Server Remotely

Suppose your laptop is running a dev server at 127.0.0.1:3000, and you want the remote side to reach it through 127.0.0.1:8080.

That is a fixed-endpoint remote forward:

import Traversio

@available(macOS 26.0, iOS 26.0, *)
func exposeLocalDashboard(configuration: SSHClientConfiguration) async throws {
    try await SSHClient.withConnection(configuration: configuration) { connection in
        try await connection.withRemotePortForwarding(
            localHost: "127.0.0.1",
            localPort: 3000,
            remoteHost: "127.0.0.1",
            remotePort: 8080
        ) { forward in
            print("Remote listener: \(forward.remoteHost):\(forward.remotePort)")

            // Keep the forward alive while the remote side needs it.
            try await Task.sleep(for: .seconds(30))
        }
    }
}

If you pass remotePort: 0, the SSH server allocates a free port and Traversio reports it back through SSHRemotePortForward.remotePort.

When To Use The Raw Listener API

Choose withRemotePortForwardListener(...) when you need details per accepted connection, for example:

  • inspect originatorHost or originatorPort
  • decide how to route each incoming connection yourself
  • speak bytes directly on SSHForwardedTCPIPChannel

The accepted channel exposes:

  • listeningHost
  • listeningPort
  • originatorHost
  • originatorPort
  • write(_:)
  • sendEOF()
  • close()
  • readChunk()
  • nextEvent()
  • events

How This Differs From Local Forwarding

This is the easiest way to avoid mixing them up:

  • local forwarding lets your local machine reach a remote service
  • remote forwarding lets the remote side reach your local service

So if someone says "the server should connect back to something on my laptop," think remote forwarding.

Support Status

As of April 7, 2026:

  • the raw remote-listener round trip is live-validated against a real local OpenSSH 9.6 target
  • the fixed-endpoint bridge data path is also live-validated against that OpenSSH target
  • the earlier scope-exit teardown warning has been cleared on the rerun local OpenSSH matrix plus lab-root
  • one accepted remote bridge failure now stays scoped to that remote connection instead of poisoning later remote clients in the same forwarding scope
  • the fixed-endpoint bridge helper can now keep multiple accepted remote connections active at the same time while the scope is open

Practical limits:

  • the helper bridges every accepted remote connection back to one fixed local endpoint
  • this API focuses on remote listeners and fixed local targets

On this page