The short answer
Yes, SSH port forwarding works from a phone. Local (-L) and remote (-R) forwarding are implemented by several iOS and Android clients, and the mechanics are identical to a desktop — the phone opens a listening port on its own loopback address and pipes it through the SSH connection.
Three things are worth knowing before you start, and only one of them is in the man page:
- Other apps on the phone can use your tunnel. Loopback is device-wide, not sandboxed per app. Forward a port in your SSH client, then open
http://127.0.0.1:8080in Safari or Chrome and it works. Apple’s local network privacy prompt doesn’t even appear, because loopback traffic never leaves the device. - Backgrounding kills it. On iOS the SSH app is suspended within roughly 20–30 seconds of leaving the foreground, and the tunnel dies with the connection. This, not the protocol, is the actual limit of mobile tunnelling.
- Dynamic (
-D) forwarding is close to useless on a phone. The SOCKS proxy opens fine; neither iOS nor Android has any system setting that can point traffic at it.
The rest of this covers each of the three forwarding types as they behave on mobile, which clients actually implement them, the server-side settings that reject tunnels, and when to stop fighting and put the machine on a private network instead.
What actually changes on a phone
The protocol doesn’t change. The environment around it does, in five specific ways:
| Desktop | Phone | |
|---|---|---|
| Tunnel lifetime | As long as the shell lives; -f -N backgrounds it indefinitely | As long as the app is in the foreground (iOS), or exempted from battery optimisation (Android) |
| Who can use the forwarded port | Any local process | Any app on the device — loopback isn’t sandboxed |
| Binding a port below 1024 | sudo | Not possible; the app is unprivileged. Use 1024+ |
| Consuming a SOCKS proxy | Browser and OS both support it | No system SOCKS setting on either OS |
| Address stability | Usually static | Carrier NAT, Wi-Fi/cellular handoff, address changes mid-session |
That last row is why -R is a bad fit on mobile and -L is fine: a local forward only needs the phone to keep one outbound connection alive, while a remote forward asks the world to keep finding a device whose network identity changes as you walk.
Local forwarding (-L): the one that works
This is 95% of legitimate mobile tunnelling. You have a service bound to the server’s own localhost — a Postgres instance, a Grafana dashboard, a container’s admin UI, a Proxmox interface on :8006 — and you want it on the phone screen.
In OpenSSH syntax, which is what you’d type in Termux, Blink Shell, or any client with a real command line:
ssh -L 8080:127.0.0.1:8080 user@server
# the phone's 127.0.0.1:8080 → the server's own 127.0.0.1:8080
Then open http://127.0.0.1:8080 in the phone’s browser, with the SSH app still running.
In a GUI client (Termius, ConnectBot) the same thing is three fields — local port, destination host, destination port — attached to a saved host. The destination is resolved from the server’s perspective, which is the part people get wrong: 127.0.0.1 there means the server’s loopback, not the phone’s.
Four mobile-specific details that save you a debugging session:
Pick a local port of 1024 or higher. Phone apps run unprivileged, so binding 80 or 443 locally fails. 8080, 8443, 3000, 5432 are all fine.
Use the same port number on both sides when tunnelling a web UI. Many admin interfaces generate absolute URLs and redirects containing their own port. Map 8006:127.0.0.1:8006 rather than 9000:127.0.0.1:8006 and a whole category of half-broken pages disappears. Name-based virtual hosts are the harder version of this problem — a server that routes on the Host header may not answer to 127.0.0.1 at all.
Bind to loopback, not 0.0.0.0. Some clients offer a “listen on all interfaces” toggle. On a phone on café Wi-Fi, that publishes your tunnel to every device on that network, unauthenticated. There is essentially no mobile use case for it.
The destination doesn’t have to be the server. -L 5432:db.internal:5432 uses the SSH server as a jump point into a network you can’t otherwise reach. That’s the same idea as ProxyJump, applied to a non-SSH service.
For the routine cases — reading logs, restarting a unit, checking Docker containers — you don’t need a tunnel at all. A plain shell does it. Reach for -L specifically when the thing you need has a web UI or a wire protocol, not a CLI. Managing a hypervisor is the classic example, and it’s why Proxmox from a phone usually starts with a forward.
Remote forwarding (-R) from a phone
-R makes a port on the server forward to a service on the phone. It is the direction people ask about second and need least.
ssh -R 9000:127.0.0.1:3000 user@server
# the server's 127.0.0.1:9000 → something listening on the phone's :3000
For this to do anything, the phone must be running a server — a local HTTP server inside a development app, say. That’s a narrow set of apps.
Two constraints on top of the mobile ones:
sshdbinds remote forwards to loopback by default.GatewayPortsisnounless changed, so-R 9000:...is reachable from the server itself and nothing else. Making it reachable from the server’s network needsGatewayPorts yesorclientspecifiedinsshd_config(sshd_config(5)) — a deliberate exposure decision, not a default to flip casually.- Remote ports below 1024 need the SSH login to be root, which it should not be. See disabling root login.
Add the phone’s shifting network identity and the 30-second foreground limit, and -R from a handset is a demo, not a workflow. If you genuinely need something on the phone reachable from elsewhere, a tailnet address is the sane answer.
Dynamic (-D / SOCKS): the one that doesn’t
ssh -D 1080 user@server turns the connection into a SOCKS proxy so you can browse as though you were on the server’s network. On a laptop this is a great trick. On a phone it lands in a wall built by both mobile OSes:
- iOS exposes an HTTP Proxy setting per saved Wi-Fi network. It accepts HTTP/HTTPS proxies only — there is no SOCKS field — it applies only to that one network, and it never applies to cellular.
- Android’s built-in Wi-Fi proxy is likewise HTTP(S) only, also per-network, with no SOCKS option.
So the tunnel opens and then nothing routes into it. Getting SOCKS applied system-wide on either OS requires an app that registers as a VPN and redirects traffic into the proxy, which is a fundamentally different kind of software from an SSH client — and if you’re going to install a VPN anyway, the SSH hop in the middle has stopped earning its place.
If the goal is “browse as if I were on that network”, use a VPN. A Tailscale exit node does exactly this, works on cellular, survives backgrounding, and needs no proxy settings. Dynamic forwarding is the one part of SSH tunnelling that mobile has genuinely made obsolete.
The real killer: backgrounding
This is the constraint that decides whether mobile tunnelling is usable for your task, so treat it as the first question rather than the footnote.
iOS. Termius states it plainly in its own support docs: the app can’t run in the background because recent iOS and iPadOS versions “stop the app’s background processes almost immediately (in 20 or 30 seconds)” (Termius support). That’s not a Termius bug; an app can request extra time via beginBackgroundTask, but the grant is short and explicitly not guaranteed. Switch to Safari to view your tunnelled dashboard and you are racing a timer.
Ways to live with it:
- iPad: Split View. Terminal on one side, browser on the other, both foreground. This is the one configuration where mobile tunnelling is genuinely comfortable, and it’s a real reason to do this work on an iPad.
- iPhone: be quick. Load the page, read it, come back. The tunnel is disposable and re-establishing it from a saved host takes a couple of seconds — design the task around that instead of fighting it.
- Or stop needing the tunnel, which is the Tailscale section below.
Android is more permissive — a client can hold a foreground service with a persistent notification — but per-OEM battery management still kills long-lived connections. Termius’s guidance is to disable battery optimisation for the app and allow unrestricted background usage (support article). On Samsung, Xiaomi and Huawei devices that setting lives in a different place each time, and it’s worth finding before you conclude the app is broken.
Either way: never run anything consequential inside a tunnelled session. The connection is expected to drop. Put long-running work in tmux on the server so its lifetime isn’t tied to your phone’s.
Which mobile clients support it
Support is real but uneven, and one of the historically popular options is no longer installable.
| Client | Platform | -L | -R | -D | Notes |
|---|---|---|---|---|---|
| Termius | iOS + Android | ✅ | ✅ | ✅ | All three types, in the free Starter plan; the add-forward form is nearly identical to desktop (docs) |
| Blink Shell | iOS/iPadOS | ✅ | ✅ | ✅ | Real OpenSSH-style flags; tunnels can be declared in the host’s SSH config so they start with the connection, and -N opens them without an interactive shell (docs) |
| ConnectBot | Android | ✅ | ✅ | ✅ | Free and open source. Long-press a saved host → Edit port forwards |
| JuiceSSH | Android | ✅ | ✅ | ✅ | Port forwarding is a Pro feature — and the app was removed from the Play Store, so new installs are a problem |
| Termux + OpenSSH | Android | ✅ | ✅ | ✅ | It’s actual OpenSSH, so every flag behaves exactly as documented. It’s also a whole Linux userland to maintain — see Termux alternatives |
| TermAI | iOS + Android | 🔜 | 🔜 | 🔜 | Not shipping yet — it’s on the roadmap. Today the answer here is the built-in Tailscale, below |
Two honest notes on that table.
Termius’s free tier really does include forwarding — it’s one of the things people wrongly assume is paywalled (free vs paid, in detail). And ConnectBot remains the best free Android option for this specific job despite having no SFTP at all; the trade-offs are in ConnectBot alternatives.
For TermAI: port forwarding is not implemented yet. We’d rather say that than quietly imply otherwise in a comparison table. The reason it hasn’t been urgent is the next section — for most of what people tunnel for, the built-in Tailscale removes the need for the tunnel entirely. When forwarding does ship, it’ll be for the cases Tailscale can’t cover: servers you don’t control and can’t install anything on.
When the server refuses the tunnel
If your client says the forward opened but connections to it immediately fail — often with channel 2: open failed: administratively prohibited: open failed — the phone is fine and the server is saying no. Three places to look, in order of likelihood:
1. The key is restricted. Options in authorized_keys apply per key, and a properly hardened phone key usually has them:
restrict,pty,from="100.64.0.0/10" ssh-ed25519 AAAAC3Nz… chen-iphone-2026-08
restrict, added in OpenSSH 7.2 (29 February 2016), disables port forwarding, agent forwarding, X11 and pty allocation in one word. pty puts the terminal back — but forwarding stays off until you explicitly re-enable it with port-forwarding, or better, whitelist exactly one destination:
restrict,pty,port-forwarding,permitopen="127.0.0.1:8080" ssh-ed25519 AAAAC3Nz… chen-iphone-2026-08
That line is a good default for a phone: a shell, plus one tunnel to one service, and nothing else. It’s the same reasoning as the rest of the mobile production safety checklist.
2. sshd_config forbids it globally. AllowTcpForwarding defaults to yes, but hardening guides and hosting providers routinely set it to no. PermitOpen restricts which destinations any client may forward to, and PermitListen does the same for -R listen requests — both default to permissive (sshd_config(5)).
3. Nothing is listening on the destination. -L 8080:127.0.0.1:8080 fails the same way whether forwarding is banned or the service simply isn’t up. Check from a shell on the server first — ss -tlnp | grep 8080 — before assuming a policy problem.
The alternative that deletes the problem
Step back and look at what a local forward is actually doing: manufacturing a route to a service you can’t otherwise address. A mesh VPN gives you that route directly, and every mobile constraint above disappears with it.
With Tailscale on the phone and the server, the service’s real address is reachable as-is:
http://100.101.102.103:8080 # or http://server-name:8080 with MagicDNS
No forwarding rule. No port-number juggling. No SSH app pinned to the foreground — the VPN runs as a system network extension, so the route survives app switching, screen lock, and the walk from Wi-Fi to cellular. Nothing is exposed to the public internet, and it works behind CGNAT, which is every mobile carrier.
For the -R case — exposing something to other machines — tailscale serve 3000 publishes a local port across the tailnet over HTTPS, and -bg keeps it running in the background across reboots (Tailscale Serve docs). Tailnet access-control rules apply to it like anything else.
This is why TermAI ships Tailscale in-app rather than assuming you’ll run two apps and argue with iOS about which one owns the VPN slot. Setup is in Tailscale SSH from a phone and reaching a home server from anywhere.
The honest limit: Tailscale needs software installed on the target machine. On a client’s box, a locked-down jump host, or a machine you’ll touch exactly once, you can’t do that — and there, SSH forwarding is the right tool and always will be.
Which one to use
| Your goal | Use | Why |
|---|---|---|
| Open a remote web UI once, on a machine you don’t own | -L, same port both sides | Nothing to install; disposable |
| Open that UI regularly, on a machine you control | Tailscale | No foreground app, no re-establishing, works on cellular |
| Reach an internal host through a jump box | -L with the internal host as destination | The SSH server is already your only way in |
| Connect a database client on the phone to a private DB | -L, local port ≥ 1024 | Wire protocols tunnel cleanly |
| Browse as if on the server’s network | Exit node, not -D | No system SOCKS setting on iOS or Android |
| Expose something running on the phone | tailscale serve, not -R | Phone addresses change; GatewayPorts is off by default |
| Just run commands | Plain SSH | Most “I need a tunnel” turns out to be journalctl |
Error messages, decoded
| What you see | What it means | Fix |
|---|---|---|
administratively prohibited: open failed | Server refused the forward | AllowTcpForwarding, PermitOpen, or restrict/no-port-forwarding on the key |
bind: Permission denied (local) | Tried to bind a port below 1024 on the phone | Use 1024 or higher |
Address already in use | Another forward or app holds that local port | Pick a different local port; close the stale session |
| Tunnel opens, browser says connection refused | Nothing listening at the destination on the server | ss -tlnp on the server; check you meant the server’s 127.0.0.1 |
| Page loads, then redirects to a broken URL | The app emits absolute URLs with its own port or hostname | Use the same port on both sides; check for name-based vhosts |
| Everything dies when you open Safari | iOS suspended the SSH app | Expected. iPad Split View, or Tailscale |
| Works on Wi-Fi, dead on cellular | You configured an HTTP proxy in Wi-Fi settings | iOS/Android proxy settings are per-Wi-Fi-network and never apply to cellular |
Warning: remote port forwarding failed | Remote port already in use, or PermitListen forbids it | Different port, or fix the server policy |
Two more that aren’t tunnel-specific but show up here constantly: a forward that dies after a few idle minutes is usually the same broken pipe that kills any idle mobile session — keepalives help. And if the connection itself won’t establish, start at connection timed out rather than debugging the forward.
FAQ
Can you do SSH port forwarding from a phone?
Yes. Local (-L) and remote (-R) forwarding both work from iOS and Android clients that implement them — Termius on both platforms, Blink Shell on iOS, ConnectBot on Android. The forwarded port opens on the phone’s own loopback address, and other apps on the phone can connect to it, because 127.0.0.1 is device-wide rather than sandboxed per app. The two real constraints are that the SSH app must stay in the foreground for the tunnel to survive, and that dynamic (-D) SOCKS forwarding has almost nothing on a phone that can consume it.
Can Safari or Chrome on my phone open a web UI through an SSH tunnel?
Yes, and it’s the main reason to tunnel from a phone at all. Set up a local forward — say 8080 to the server’s 127.0.0.1:8080 — then open http://127.0.0.1:8080 in the browser. Apple treats loopback as exempt from the local network privacy prompt because that traffic never leaves the device, so no permission dialog appears and no extra configuration is needed. Keep the SSH app running: on iOS, switching away from it closes the tunnel within about 30 seconds.
Why does my SSH tunnel die when I switch apps on iPhone? Because iOS suspends backgrounded apps. Termius documents that recent iOS and iPadOS versions stop the app’s background processes in roughly 20 to 30 seconds, which closes the SSH connection and every tunnel riding on it. There’s no app-side fix — the background execution grant is short and not guaranteed. On iPad, keep the SSH app on screen in Split View next to the browser. On iPhone, do the tunnelled work quickly, or use a VPN-based approach like Tailscale, where the network stays up because the VPN runs as a system extension rather than as your app.
Does dynamic forwarding (ssh -D) work on iOS or Android?
The tunnel opens, but almost nothing on the phone can use it. iOS Wi-Fi settings accept an HTTP proxy only, apply it per saved Wi-Fi network, and never apply it to cellular; there is no system SOCKS setting. Android’s built-in Wi-Fi proxy is likewise HTTP(S) only. Consuming a SOCKS proxy system-wide on either OS requires an app that registers as a VPN and redirects traffic into it — a different class of tool than an SSH client. If the goal is browsing as though you were on the server’s network, use a VPN or a Tailscale exit node, not -D.
Why do I get “administratively prohibited: open failed”?
The server refused the forwarding request, and it’s nearly always one of three server-side settings rather than anything on the phone. AllowTcpForwarding may be no in sshd_config; PermitOpen may whitelist destinations that don’t include yours; or the key you authenticated with may carry restrict or no-port-forwarding in its authorized_keys line. Restricted phone keys are a common cause, since restrict disables port forwarding along with agent forwarding, X11 and pty allocation in a single word.
Should I use an SSH tunnel or Tailscale to reach a private service from my phone? Tailscale, for anything you do more than once. A tunnel is a per-session workaround for a service you can’t route to; a tailnet makes the service routable, so the phone opens the real address and port with no forwarding rule, no SSH app pinned to the foreground, and no tunnel to re-establish after every drop. Keep SSH forwarding for one-off access to a machine you can’t or won’t install anything on — a client’s server, a jump host, a box you’ll touch once.
Quick facts
-Lworks well on mobile,-Rrarely makes sense,-Dhas nothing to consume it.- Loopback is not sandboxed — any app on the phone can use the forwarded port, and no iOS local-network permission prompt appears, because loopback traffic never leaves the device.
- Bind local ports ≥ 1024. Phone apps are unprivileged.
- iOS suspends the app in ~20–30 seconds of backgrounding; the tunnel goes with it. iPad Split View is the workable configuration.
- Android needs battery optimisation disabled for the SSH app, or OEM power management kills long-lived tunnels.
sshdbinds-Rto loopback by default (GatewayPorts no).restrictinauthorized_keysturns forwarding off; re-enable narrowly withport-forwardingpluspermitopen="host:port".- Clients that support it: Termius (iOS/Android, free tier), Blink Shell (iOS), ConnectBot (Android), Termux + OpenSSH. TermAI: on the roadmap, not shipping.
- Tailscale removes the need for most mobile tunnels — real addresses, survives backgrounding, works on cellular.
Free on iOS and Android. 5 AI requests/day on the free tier, plus unlimited SSH/SFTP and built-in Tailscale.