Skip to main content

Remote Port Forwarding (-R)

Remote port forwarding (SSH -R flag) creates a secure tunnel that exposes services running on your local machine to a remote server, allowing the server to access your local resources.

What is Remote Port Forwarding?

Simple Explanation: You have a service running on your computer (like a web app in development) and you want people or systems on a remote server to access it. Remote port forwarding creates a tunnel that makes your local service accessible from the remote server.

Flow:

Remote App → Remote Server:REMOTE_PORT → SSH Tunnel → localhost:LOCAL_PORT → Your App

Example:

Remote Server:8080 → SSH Tunnel → Your Computer:3000 (Local dev server)

Now applications on the remote server can access localhost:8080 and it connects to your local dev server on port 3000!

Why Use Remote Port Forwarding?

Common Use Cases

Development & Testing

  • Share local development work with team
  • Demo applications before deployment
  • Test webhooks locally
  • Preview work to clients

Temporary Access

  • Grant temporary access to local services
  • Debug production issues with local tools
  • Test integrations without deployment
  • Run diagnostics remotely

Reverse Tunneling

  • Bypass NAT/firewall restrictions
  • Access home services from work
  • Remote access to internal networks
  • IoT device management

Webhook Development

  • Receive webhooks on localhost
  • Test payment gateways (Stripe, PayPal)
  • Test OAuth callbacks
  • Test API integrations

Creating Remote Port Forwarding

From Port Forwarding Panel

  1. Open Port Forwarding in sidebar
  2. Click "New Tunnel"
  3. Select "Remote" type
  4. Fill in configuration:
┌──────────────────────────────────────────────┐
│ New Port Forwarding Tunnel │
├──────────────────────────────────────────────┤
│ Name: * │
│ [Share Local Dev Server ] │
│ │
│ Connection: * │
│ [Development Server ▼] │
│ │
│ Type: * │
│ [ ] Local [●] Remote [ ] Dynamic │
│ │
│ ─── Remote Configuration ─── │
│ Remote Host: * │
│ [0.0.0.0 ] ← Listen on all IPs │
│ │
│ Remote Port: * │
│ [8080 ] │
│ │
│ ─── Local Configuration ─── │
│ Local Host: │
│ [localhost ] │
│ │
│ Local Port: * │
│ [3000 ] ← Your local app │
│ │
│ ─── Options ─── │
│ [✓] Auto-start with connection │
│ [ ] Start immediately │
│ │
│ Description: │
│ [Share React dev server with team ] │
│ │
│ [Cancel] [Create Tunnel] │
└──────────────────────────────────────────────┘
  1. Click "Create Tunnel"
  2. Click "Start" to activate

Understanding Configuration

Name

Descriptive name for the tunnel:

✓ "Share Local Dev Server"
✓ "Webhook Testing Endpoint"
✓ "Remote Access to Local API"

✗ "Tunnel 1"
✗ "Remote"

Remote Host

Interface on remote server to bind to:

Options:

0.0.0.0 (all interfaces)

Anyone on remote server's network can access
Remote server can forward externally
Most flexible

127.0.0.1 (localhost only)

Only processes on remote server can access
More secure
Applications on same server only

Specific IP

192.168.1.100
Bind to specific interface
Advanced use case

Important: Remote server's sshd must allow this!

# In /etc/ssh/sshd_config
GatewayPorts yes # Allows 0.0.0.0
# or
GatewayPorts clientspecified # Allows client to choose

Remote Port

Port on remote server that will listen:

Examples:

  • 8080 - Common web port
  • 9000 - Custom app port
  • 3000 - Node.js default
  • 5000 - Flask default

Important:

  • Must be available on remote server
  • Ports < 1024 require root on server
  • Check firewall rules

Local Host

Target on your local machine:

Options:

localhost (most common)

Service on your own computer
Example: Your dev server

127.0.0.1

Same as localhost
More explicit

Local network IP

192.168.1.50
Service on another device on your network
Advanced use case

Local Port

Port of your local service:

Common Ports:

  • 3000 - React, Express default
  • 8000 - Django, Python HTTP
  • 5000 - Flask default
  • 4200 - Angular default
  • 8080 - Common development
  • 3001 - Next.js default

Real-World Examples

Example 1: Share React Development Server

Scenario: Demo React app to remote team

Local Setup:

# Start React dev server
cd my-react-app
npm start
# Server runs on localhost:3000

Tunnel Setup:

Name: React Dev Demo
Type: Remote
Remote Host: 0.0.0.0
Remote Port: 8080
Local Host: localhost
Local Port: 3000

Usage:

Remote server users can access:
http://SERVER_IP:8080

They see your local React app!

Use Case:

  • Team reviews new features
  • Client previews before deployment
  • Cross-browser testing on server
  • Mobile device testing

Example 2: Webhook Testing (Stripe, PayPal)

Scenario: Test payment webhooks locally

Local Setup:

# Your local API server
cd my-api
npm start
# Listening on localhost:3000
# Webhook endpoint: /api/webhooks/stripe

Tunnel Setup:

Name: Stripe Webhook Testing
Type: Remote
Remote Host: 0.0.0.0
Remote Port: 9000
Local Host: localhost
Local Port: 3000

Stripe Configuration:

Webhook URL: http://YOUR_SERVER_IP:9000/api/webhooks/stripe

Usage:

# Stripe sends webhook to remote server
# Tunnel forwards to your localhost:3000
# You can debug in real-time!

Benefits:

  • Test webhooks without deployment
  • Debug with live data
  • Faster development cycle
  • No need for ngrok/similar services

Example 3: Local Database Access from Remote

Scenario: Remote scripts need to query your local database

Local Setup:

# PostgreSQL running locally
# localhost:5432

Tunnel Setup:

Name: Local PostgreSQL Access
Type: Remote
Remote Host: 127.0.0.1 # Only on remote server
Remote Port: 5432
Local Host: localhost
Local Port: 5432

Usage on Remote Server:

# On remote server
psql -h localhost -p 5432 -U myuser mydb

# Connects to YOUR local PostgreSQL!

Use Case:

  • Data migration scripts
  • Analytics queries
  • Backup jobs
  • Development testing

Example 4: Internal Tool Access

Scenario: Expose local admin panel to remote team

Local Setup:

# Admin dashboard on localhost:8000
python manage.py runserver

Tunnel Setup:

Name: Admin Dashboard Access
Type: Remote
Remote Host: 0.0.0.0
Remote Port: 8888
Local Host: localhost
Local Port: 8000

Usage:

Team accesses: http://SERVER_IP:8888
See your local admin dashboard

Example 5: IoT Device Management

Scenario: Control IoT device through remote server

Local Setup:

# Home automation server
# localhost:8123 (Home Assistant)

Tunnel Setup:

Name: Home Automation Remote Access
Type: Remote
Remote Host: 127.0.0.1
Remote Port: 8123
Local Host: localhost
Local Port: 8123

Usage:

From anywhere:
1. SSH to remote server
2. Access localhost:8123
3. Control home devices

Example 6: Collaborative Development

Scenario: Multiple developers share local services

Developer A Setup:

Name: Dev A - Backend API
Remote Port: 9001
Local Port: 3000 (API server)

Developer B Setup:

Name: Dev B - Frontend
Remote Port: 9002
Local Port: 3001 (React app)

Developer C Setup:

Name: Dev C - Database
Remote Port: 9003
Local Port: 5432 (PostgreSQL)

Integration:

All developers forward to same remote server
Server acts as integration point
Each service accessible at different port
Test full stack integration!

Example 7: Client Demo Environment

Scenario: Quick demo without deployment

Setup:

Name: Client Demo - Full App
Type: Remote
Remote Host: 0.0.0.0
Remote Port: 80 (requires sudo on server)
Local Host: localhost
Local Port: 3000

Demo URL:

http://SERVER_IP
# Or with domain:
http://demo.yourcompany.com

Advantages:

  • No deployment needed
  • Show latest development
  • Quick iterations
  • Easy updates

Example 8: Testing Mobile Apps

Scenario: Test mobile app API endpoints

Local Setup:

# Mobile backend API
cd mobile-backend
npm start # Port 8000

Tunnel Setup:

Name: Mobile Backend API
Type: Remote
Remote Host: 0.0.0.0
Remote Port: 8000
Local Host: localhost
Local Port: 8000

Mobile App Config:

API_URL=http://YOUR_SERVER_IP:8000/api

Testing:

  • Real devices can access API
  • Test over cellular networks
  • Different network conditions
  • Multiple devices simultaneously

Advanced Scenarios

Multiple Local Services Exposed

Scenario: Expose multiple services through one SSH connection

Setup:

Tunnel 1:
Name: API Server
Remote: 0.0.0.0:9001 → Local: localhost:3000

Tunnel 2:
Name: Frontend
Remote: 0.0.0.0:9002 → Local: localhost:3001

Tunnel 3:
Name: WebSocket
Remote: 0.0.0.0:9003 → Local: localhost:3002

All run through single SSH connection!

Chaining Through Jump Hosts

Scenario: Expose local service through bastion host

Network:

Your Computer:3000
↓ SSH
Bastion Host
↓ Accessible from
Public Internet

Setup:

Host: Bastion
Type: Remote
Remote Host: 0.0.0.0
Remote Port: 8080
Local Host: localhost
Local Port: 3000

Access:

Anyone can access: http://BASTION_IP:8080

Exposing Network Services

Scenario: Expose service on another local network device

Network:

Your Computer
↓ Same network
Raspberry Pi (192.168.1.50:8080)

Tunnel Setup:

Name: Expose Raspberry Pi
Type: Remote
Remote Host: 0.0.0.0
Remote Port: 8080
Local Host: 192.168.1.50 ← Different local device
Local Port: 8080

Result: Remote users access your Pi through server!

Public Demo with Domain

Scenario: Professional demo with custom domain

DNS Setup:

demo.yourcompany.com → YOUR_SERVER_IP

Nginx on Remote Server:

server {
listen 80;
server_name demo.yourcompany.com;

location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
}
}

Tunnel:

Remote Host: 127.0.0.1  # Only Nginx accesses
Remote Port: 8080
Local Port: 3000

Result: https://demo.yourcompany.com shows your local app!

Server Configuration

SSH Server Setup

Remote forwarding requires server configuration:

File: /etc/ssh/sshd_config

# Allow remote port forwarding
GatewayPorts yes
# or
GatewayPorts clientspecified

# Optional: Limit which ports can be forwarded
PermitOpen any
# or restrict:
PermitOpen localhost:8080
PermitOpen localhost:9000-9010

Restart SSH:

sudo systemctl restart sshd

Firewall Configuration

Open ports on remote server:

Ubuntu/Debian (UFW):

sudo ufw allow 8080/tcp
sudo ufw reload

CentOS/RHEL (firewalld):

sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload

Check if port is listening:

sudo netstat -tlnp | grep 8080
# or
sudo ss -tlnp | grep 8080

Troubleshooting

Port Bind Failed

Error: "Cannot bind to port on remote server"

Causes:

  1. Port already in use on server
  2. GatewayPorts not enabled
  3. Port below 1024 without root
  4. PermitOpen restricts the port

Solutions:

Check if port is used:

# On remote server
sudo lsof -i :8080
sudo netstat -tlnp | grep 8080

Enable GatewayPorts:

# Edit /etc/ssh/sshd_config
sudo nano /etc/ssh/sshd_config

# Add or modify:
GatewayPorts yes

# Restart SSH
sudo systemctl restart sshd

Use different port:

Change: Remote Port: 9080

Check PermitOpen:

# In sshd_config
grep PermitOpen /etc/ssh/sshd_config

Connection Refused from Remote

Error: Remote users get "Connection refused"

Causes:

  1. Firewall blocking port
  2. Wrong remote host (127.0.0.1 vs 0.0.0.0)
  3. Local service not running
  4. Tunnel stopped

Debug Steps:

1. Check tunnel is active:

Xermius Port Forwarding panel
Status should show: ● Active

2. Test from server locally:

# SSH to server
ssh user@server

# Test connection
curl http://localhost:8080
# Should work if tunnel is up

3. Check firewall:

sudo ufw status | grep 8080

4. Check remote host binding:

Should be 0.0.0.0 for external access
127.0.0.1 only works locally on server

Local Service Not Responding

Issue: Tunnel connects but requests fail

Causes:

  1. Local service not running
  2. Wrong local port
  3. Service only binding to specific IP
  4. Local firewall blocking

Solutions:

Check local service:

# On your computer
curl http://localhost:3000
# Should return response

Check service is running:

# macOS/Linux
lsof -i :3000

# Windows
netstat -ano | findstr :3000

Check service binding:

# Make sure service listens on 0.0.0.0 or 127.0.0.1
netstat -tlnp | grep 3000

# Good: 0.0.0.0:3000 or 127.0.0.1:3000
# Bad: 192.168.1.100:3000 (specific IP only)

Tunnel Keeps Disconnecting

Issue: Tunnel drops frequently

Causes:

  • Network instability
  • Server closes idle connections
  • Firewall timeout
  • SSH keepalive not configured

Solutions:

Enable keepalive in Xermius:

Connection settings:
[✓] Enable keep-alive
Interval: 30 seconds

SSH config on your computer:

# ~/.ssh/config
Host yourserver
ServerAliveInterval 30
ServerAliveCountMax 3

SSH config on server:

# /etc/ssh/sshd_config
ClientAliveInterval 30
ClientAliveCountMax 3

Performance Issues

Issue: Slow response through tunnel

Causes:

  • High latency connection
  • Large data transfers
  • CPU overhead
  • Network congestion

Solutions:

1. Enable compression:

Xermius settings:
[✓] Enable SSH compression

2. Optimize application:

  • Minimize data sent
  • Use caching
  • Compress responses
  • Reduce assets size

3. Check latency:

ping server-ip

4. Use faster network:

  • Wired vs WiFi
  • Better internet connection
  • Closer server location

Security Considerations

1. Understand Exposure

Remote forwarding exposes your local service:

⚠ Anyone on remote server's network can access
⚠ If server is compromised, your service is exposed
⚠ Data travels through server

2. Use Localhost Binding When Possible

✓ Remote Host: 127.0.0.1  # Only on server
✗ Remote Host: 0.0.0.0 # Accessible from anywhere

3. Add Authentication

Protect exposed services:

✓ HTTP Basic Auth
✓ API keys/tokens
✓ IP whitelisting
✓ Session authentication

4. Use Firewall Rules

Restrict access on remote server:

# Only allow specific IPs
sudo ufw allow from 192.168.1.0/24 to any port 8080

5. Temporary Tunnels

Close when not needed:

✓ Start tunnel
✓ Do your work
✓ Stop tunnel
✓ Delete if one-time use

6. Monitor Access

Check who's accessing:

# On remote server
sudo tail -f /var/log/nginx/access.log
# or
sudo tail -f /var/log/syslog | grep sshd

7. Use HTTPS/TLS

Encrypt data beyond SSH tunnel:

✓ HTTPS for web apps
✓ TLS for APIs
✓ Encrypted protocols

8. Audit Regularly

Review active tunnels:

  • Which ports are exposed?
  • Who has access?
  • Are they still needed?
  • Any suspicious activity?

Best Practices

1. Descriptive Names

✓ "Webhook Testing - Stripe"
✓ "Client Demo - Q4 2024"
✓ "Local API - Mobile Team"

✗ "Remote Forward"
✗ "Tunnel 2"

2. Document Exposed Services

Description:
Exposes local React dev server (port 3000)
For team review and testing
Remote access: http://SERVER_IP:8080
Contact: dev-team@company.com

3. Use Non-Standard Ports

✓ Remote Port: 9080
✓ Remote Port: 8888
✗ Remote Port: 80 (requires root)
✗ Remote Port: 22 (conflicts with SSH)

4. Plan Port Assignments

Team Port Map:
9001 - Backend API (Dev A)
9002 - Frontend (Dev B)
9003 - Database (Dev C)
9004 - Cache (Dev D)

5. Clean Up After Demos

After demo:
1. Stop tunnel
2. Delete if one-time
3. Document if recurring
4. Update team

6. Test Before Presenting

Pre-demo checklist:
☐ Tunnel is running
☐ Service is accessible
☐ URL works
☐ No errors in logs
☐ Performance is acceptable

7. Have Backup Plan

If tunnel fails during demo:
- Screen recording ready
- Backup demo environment
- Alternative access method

Common Patterns

Development Team Pattern

Each developer:
- Runs local services
- Forwards to shared server
- Uses unique port range

Server acts as:
- Integration point
- Demo environment
- Testing platform

Client Demo Pattern

Setup:
- Forward to presentable URL
- Use custom domain
- Add SSL if possible

Benefits:
- Professional appearance
- Easy updates
- Quick iterations

Webhook Testing Pattern

Flow:
- Service sends webhook
- Received on remote server
- Forwarded to local dev

Advantages:
- Test with real webhooks
- Debug locally
- No deployment needed

IoT Management Pattern

Setup:
- Home services behind NAT
- Forward through VPS
- Access from anywhere

Use cases:
- Home automation
- Security cameras
- Network storage

Alternatives & Comparisons

Xermius vs Ngrok

Xermius:

✓ Use your own server
✓ No monthly limits
✓ Full control
✓ Custom domains
✓ No external service
✗ Requires SSH server

Ngrok:

✓ No server needed
✓ Easy setup
✓ Free tier available
✗ Monthly limits
✗ Random URLs (free tier)
✗ External service dependency

Xermius vs LocalTunnel

Xermius:

✓ More secure (your server)
✓ Better performance
✓ No rate limits
✓ Professional setup

LocalTunnel:

✓ Extremely simple
✓ No account needed
✗ Public subdomain only
✗ Less reliable

Next Steps