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
- Open Port Forwarding in sidebar
- Click "New Tunnel"
- Select "Remote" type
- 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] │
└──────────────────────────────────────────────┘
- Click "Create Tunnel"
- 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 port9000- Custom app port3000- Node.js default5000- 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:
- Port already in use on server
- GatewayPorts not enabled
- Port below 1024 without root
- 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:
- Firewall blocking port
- Wrong remote host (127.0.0.1 vs 0.0.0.0)
- Local service not running
- 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:
- Local service not running
- Wrong local port
- Service only binding to specific IP
- 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