When dealing with IoT devices like Raspberry Pi that frequently change networks, traditional port forwarding becomes impractical. The device needs to maintain a persistent connection that survives network transitions while remaining secure.
The most reliable approach is establishing a reverse SSH tunnel with these characteristics:
# On the Raspberry Pi (client side)
autossh -M 0 -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" \
-R 8080:localhost:80 -i ~/.ssh/tunnel_key user@rpi.example.com
On your public server (rpi.example.com), configure SSH to handle persistent connections:
# /etc/ssh/sshd_config
ClientAliveInterval 60
TCPKeepAlive yes
GatewayPorts clientspecified
Create a systemd service on the Raspberry Pi for automatic reconnection:
# /etc/systemd/system/ssh-tunnel.service
[Unit]
Description=AutoSSH tunnel service
After=network.target
[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 -N -o "ExitOnForwardFailure=yes" \
-o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" \
-R *:8080:localhost:80 -i /home/pi/.ssh/tunnel_key user@rpi.example.com
Restart=always
RestartSec=60
[Install]
WantedBy=multi-user.target
While reverse SSH works well, consider these alternatives for specific cases:
- Cloudflare Tunnels: Better for web services with HTTPS requirements
- ngrok: Ideal for temporary development tunnels
- WireGuard VPN: More efficient for high-bandwidth applications
Key problems and solutions:
# Check tunnel status
ssh -T user@rpi.example.com "netstat -tulpn | grep 8080"
# Debug connection issues
autossh -v -M 0 -N -R 8080:localhost:80 user@rpi.example.com
Essential security measures:
# On the public server, restrict SSH access
# /etc/ssh/sshd_config
AllowUsers tunnel_user
PasswordAuthentication no
PermitTunnel yes
When your Raspberry Pi (or any Linux device) frequently changes networks and sits behind NAT/firewalls, accessing its HTTP server becomes challenging. The device's dynamic IP address and network restrictions make traditional port forwarding unreliable.
Reverse tunneling creates a persistent pathway from your public server (rpi.example.com) back to your local device. Unlike regular SSH tunneling where the client initiates the connection to access remote resources, here the resource provider (your Pi) initiates the connection to the public server.
# Basic reverse tunnel command to run on your Pi:
ssh -N -R 8080:localhost:80 user@rpi.example.com
1. Public Server Configuration
On rpi.example.com, ensure these SSH settings in /etc/ssh/sshd_config:
GatewayPorts yes
ClientAliveInterval 60
ClientAliveCountMax 3
Then restart SSH: sudo systemctl restart sshd
2. Local Device Setup
Create an autostart script on your Pi (/etc/systemd/system/reverse-tunnel.service):
[Unit]
Description=Reverse SSH Tunnel
After=network.target
[Service]
ExecStart=/usr/bin/ssh -NT -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes \
-R *:8080:localhost:80 -i /home/pi/.ssh/tunnel_key user@rpi.example.com
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Enable with:
sudo systemctl daemon-reload
sudo systemctl enable reverse-tunnel
sudo systemctl start reverse-tunnel
3. HTTP Forwarding
On rpi.example.com, configure Nginx to forward requests:
server {
listen 80;
server_name rpi.example.com;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Connection Stability
For unreliable networks, consider these ssh options:
-o TCPKeepAlive=yes \
-o ServerAliveInterval=15 \
-o ServerAliveCountMax=3 \
Security Enhancements
1. Use SSH keys with passphrase protection
2. Restrict port binding to specific interfaces
3. Implement fail2ban on both ends
While reverse SSH works well, other approaches exist:
- Cloudflare Tunnels (formerly Argo)
- Ngrok/Tailscale
- WireGuard VPN with static IP
The SSH method remains advantageous for its simplicity, no additional dependencies, and compatibility with existing infrastructure.