Back to tutorials
Tutorial

SSH Port Forwarding Tutorial (2026): Securely Access cPanel, Webmail, and Database Ports on a VPS Without Exposing Them

SSH port forwarding tutorial (2026) to reach cPanel, webmail, and admin ports safely without opening public firewall rules.

By Anurag Singh
Updated on Jul 23, 2026
Category: Tutorial
Share article
SSH Port Forwarding Tutorial (2026): Securely Access cPanel, Webmail, and Database Ports on a VPS Without Exposing Them

You don’t need to publish WHM/cPanel, webmail, phpMyAdmin, or internal admin panels to the public internet just to manage a server. In this SSH port forwarding tutorial, you’ll set up local tunnels (plus a small hardening layer). Only authenticated SSH users will be able to reach sensitive ports on your VPS or dedicated server.

The idea is simple: keep ports like 2087, 2083, 2096, 10000, 3000, or 8080 off the public edge. You can still use them normally from your laptop.

You’ll also get a rollout checklist for resellers and teams, plus troubleshooting steps for the failures you’ll actually see.

What you’ll build (and why it matters)

  • Local forwarding for single-user access (your laptop → server → private service)
  • Remote forwarding for controlled callback access (useful in odd networks)
  • Autossh/systemd so tunnels survive Wi‑Fi drops
  • Firewall rules that keep admin ports closed publicly
  • SSH hardening so tunnels aren’t a backdoor

This pattern is popular in hosting ops for one reason: it shrinks your attack surface fast.

If an admin panel has a zero-day, it’s much harder to exploit when the port isn’t reachable.

Prerequisites and safe defaults

This tutorial assumes:

  • A VPS or dedicated server running Ubuntu 24.04 LTS / Debian 12 / AlmaLinux 9+.
  • You can SSH as a sudo user (avoid daily root logins).
  • The service you want to reach is already running on the server (cPanel/WHM, Roundcube, an internal admin UI, etc.).

If SSH and your firewall aren’t locked down yet, do that first. These two guides cover the basics without breaking access:

If you want the same end result without maintaining panels and base OS policy yourself, managed VPS hosting from HostMyCode can handle OS, SSH, and firewall maintenance.

You stay focused on sites and clients.

Step 1: Confirm which ports you should NOT expose publicly

First, check what’s listening on the server. Run either command:

sudo ss -lntp
# or
sudo lsof -iTCP -sTCP:LISTEN -P -n

Common ports to tunnel instead of publishing:

  • WHM: 2087 (HTTPS)
  • cPanel: 2083 (HTTPS)
  • Webmail: 2096 (HTTPS)
  • Admin UIs: 10000 (Webmin), 3000/8080 (custom apps), 5601 (Kibana), 9000 (various dashboards)

If you run mail on the same server, keep SMTP/IMAP public only when you truly need remote clients.

If it’s “admin-only access,” tunnel those ports too.

Step 2: Lock sensitive services to localhost (best-case) or private IP (good-case)

Port forwarding works best when the service listens only on 127.0.0.1 (or a private interface).

If firewall rules drift later, the service still won’t be reachable from the internet.

cPanel/WHM note

On cPanel servers, you usually restrict access through WHM security controls and firewall policy. You don’t typically change bind addresses.

The principle stays the same: don’t leave panel ports open unless you have a clear business reason.

Example: bind a custom admin UI to localhost

If you run an internal app, bind it to localhost. In many Node.js apps, that’s as simple as:

# app config
HOST=127.0.0.1
PORT=3000

Restart the service. Then confirm it’s listening only on localhost:

sudo ss -lntp | grep :3000

Step 3: Close admin ports in your firewall (leave SSH open)

With UFW, a solid baseline is to allow SSH, HTTP, and HTTPS. Keep everything else closed. Example:

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# explicitly deny common admin ports (optional but clear)
sudo ufw deny 2087/tcp
sudo ufw deny 2083/tcp
sudo ufw deny 2096/tcp
sudo ufw deny 10000/tcp

sudo ufw status verbose

If you’re on cPanel with CSF, make the equivalent changes there. This guide walks through a safe setup: cPanel CSF firewall setup.

After you close these ports publicly, the SSH tunnel becomes the only supported path for admin access.

Step 4: Create a local SSH tunnel (the one you’ll use most)

Local forwarding maps a port on your laptop to a destination the server can reach.

Syntax:

ssh -L LOCAL_PORT:DEST_HOST:DEST_PORT user@SERVER_IP

Example A: WHM (2087) through SSH

From your laptop:

ssh -L 12087:127.0.0.1:2087 admin@203.0.113.10

Then open WHM in your browser:

https://127.0.0.1:12087

Your browser talks to your own machine. SSH carries that traffic to the server.

The server then connects to 127.0.0.1:2087.

Example B: cPanel (2083) and Webmail (2096)

# cPanel
ssh -L 12083:127.0.0.1:2083 admin@203.0.113.10
# Webmail
ssh -L 12096:127.0.0.1:2096 admin@203.0.113.10

If you run Roundcube separately, keep DNS and TLS consistent. This guide helps: this Roundcube webmail setup guide.

Example C: tunnel a private service on another host (jump through the VPS)

You can also forward onward to a private network resource the VPS can reach.

This is common with dedicated servers plus a private VLAN:

ssh -L 15432:10.10.0.25:5432 admin@203.0.113.10

Now your laptop can reach 10.10.0.25:5432 via localhost:15432. You did that without opening database ports to the internet.

Step 5: Make the tunnel safer and less annoying

Long SSH commands get tedious. Put your settings in an SSH config stanza.

You’ll bring tunnels up the same way every time.

Use ~/.ssh/config

Host hostmycode-vps
  HostName 203.0.113.10
  User admin
  IdentityFile ~/.ssh/id_ed25519
  ServerAliveInterval 30
  ServerAliveCountMax 3

Host hostmycode-whm-tunnel
  HostName 203.0.113.10
  User admin
  IdentityFile ~/.ssh/id_ed25519
  LocalForward 12087 127.0.0.1:2087
  LocalForward 12083 127.0.0.1:2083
  LocalForward 12096 127.0.0.1:2096
  ExitOnForwardFailure yes
  ServerAliveInterval 30
  ServerAliveCountMax 3

Bring up all forwards with one command:

ssh hostmycode-whm-tunnel

If you don’t need a shell, add -N:

ssh -N hostmycode-whm-tunnel

Background mode (use with care)

If you want SSH to move into the background after you authenticate:

ssh -fN hostmycode-whm-tunnel

Later, find it with ps aux | grep ssh. End it by killing the process.

Step 6: Keep tunnels up reliably with autossh + systemd (admin-friendly)

In real life, tunnels die because laptops sleep, Wi‑Fi drops, or networks get flaky.

autossh restarts tunnels when they break. That makes tunnel access much easier to support for teams and resellers.

Install autossh (Linux workstation)

# Ubuntu/Debian
sudo apt update && sudo apt install -y autossh

Create a user systemd unit

Create ~/.config/systemd/user/whm-tunnel.service:

[Unit]
Description=SSH tunnel for WHM/cPanel/Webmail
After=network-online.target

[Service]
ExecStart=/usr/bin/autossh -M 0 -N \
  -o "ExitOnForwardFailure=yes" \
  -o "ServerAliveInterval=30" \
  -o "ServerAliveCountMax=3" \
  -L 12087:127.0.0.1:2087 \
  -L 12083:127.0.0.1:2083 \
  -L 12096:127.0.0.1:2096 \
  admin@203.0.113.10
Restart=always
RestartSec=5

[Install]
WantedBy=default.target

Enable and start it:

systemctl --user daemon-reload
systemctl --user enable --now whm-tunnel.service
systemctl --user status whm-tunnel.service

At that point, your local ports stay mapped even if the connection blips.

Step 7: Add guardrails in sshd_config so forwarding can’t be abused

Port forwarding is powerful. Treat it like a privileged capability.

Enable it on purpose, then limit who gets it and what else they can do.

Recommended sshd hardening for hosting servers

Edit /etc/ssh/sshd_config (same path on most distros):

sudoedit /etc/ssh/sshd_config

Consider these settings (adjust for your policy):

# Disable password logins if you can
PasswordAuthentication no

# Reduce risk surface
PermitRootLogin no
AllowTcpForwarding yes
X11Forwarding no
PermitTunnel no
GatewayPorts no

# Optional: restrict who can SSH at all
AllowUsers admin resellerops

Reload SSH safely (don’t drop your current session):

sudo systemctl reload ssh
# On some systems:
# sudo systemctl reload sshd

If you want stricter isolation, create a dedicated “tunnel-only” user. Lock it down with Match User blocks.

Keep full shells limited to your admin accounts.

Step 8: Verify the tunnel end-to-end (don’t trust the browser)

Before you call it done, confirm two things:

  • The service is closed publicly.
  • The service is reachable through the tunnel.

Check from an external network (public reachability)

From another host on the internet:

nc -vz 203.0.113.10 2087
nc -vz 203.0.113.10 2083
nc -vz 203.0.113.10 2096

These should fail (timeout/refused). Port 22 should succeed.

Check locally (tunnel reachability)

On your laptop (with tunnel active):

nc -vz 127.0.0.1 12087
nc -vz 127.0.0.1 12083
nc -vz 127.0.0.1 12096

Then load the HTTPS URL. You may see a browser warning because the certificate doesn’t match 127.0.0.1. That’s normal for admin-only tunnels.

If you want fewer warnings, you can tunnel to the server hostname and use SNI-aware tooling.

Many teams accept the mismatch for internal endpoints.

Common failure cases (and fixes that work)

Most port forwarding problems are boring—and predictable. These are the ones that show up repeatedly.

“bind: Address already in use”

Your chosen local port is already taken. Pick a different local port:

ssh -L 22087:127.0.0.1:2087 admin@203.0.113.10

To see what’s holding it:

lsof -iTCP:12087 -sTCP:LISTEN -n -P

“channel open failed: connect failed: Connection refused”

SSH connected, but the server couldn’t connect to the destination host:port. Common causes:

  • The service isn’t listening (or it’s listening on a different port).
  • You forwarded to 127.0.0.1 but the service only listens on the public IP (or vice versa).
  • A local firewall on the server blocks loopback or private traffic (less common, but it happens).

Re-check the server side:

sudo ss -lntp | egrep ':(2087|2083|2096)'
curl -kI https://127.0.0.1:2087/

Tunnel connects, but the page spins or drops

This usually points to unstable networks, aggressive middleboxes, or MTU quirks on certain connections.

Try:

  • Use autossh as shown above.
  • Add -o IPQoS=throughput on some restricted networks.
  • Prefer a bastion/jump host pattern if you manage many servers.

If you’re moving admin access behind a jump host, the approach in this SSH jump host tutorial pairs well with forwarding.

HTTPS certificate warnings in the browser

With port forwarding, the URL is often https://127.0.0.1:12087. The certificate was issued for your server hostname, not 127.0.0.1.

Options:

  • Accept the warning (admin-only, low frequency).
  • Use a browser profile dedicated to admin tasks.
  • Ensure your TLS setup is correct so warnings are only about hostname mismatch (not chain problems). If you suspect TLS problems, use this TLS handshake troubleshooting guide.

Operational checklist: how to roll this out safely on a hosting server

  • Inventory admin ports with ss -lntp and decide what can be private-only.
  • Restrict services to localhost/private IP where practical.
  • Close ports at the firewall (UFW/CSF), leaving SSH and web ports open as needed.
  • Require SSH keys; disable password auth if your workflow allows it.
  • Create ~/.ssh/config stanzas for each tunnel.
  • Add autossh/systemd for anyone who needs stable access.
  • Document “break-glass” steps: how to temporarily allow a port from a fixed IP if SSH access is down.

Where this fits with HostMyCode hosting

If you run client sites on a VPS, tunnels let you keep panels and admin UIs private. You get that security without slowing down daily work.

A HostMyCode VPS gives you the OS-level control to apply this pattern cleanly, and it scales nicely as you add services.

If you’d rather not own SSH policy, firewall rules, and panel patch cycles, managed VPS hosting is the simpler route.

You control who gets access, and routine maintenance stops being a recurring liability.

If you’re exposing WHM, webmail, or other admin ports just to make daily work easier, flip the model: close those ports and use SSH tunnels instead. Start with a HostMyCode VPS, or choose managed VPS hosting if you want ongoing help with hardening and updates.

FAQ

Do SSH tunnels replace a VPN for server administration?

For single-service admin access, yes, in many cases. A VPN is a better fit when you need broad network access (many hosts/ports) and device posture controls.

Can I use SSH port forwarding for cPanel even if panel ports are blocked publicly?

Yes. That’s the point. As long as SSH (22/tcp) is reachable and you can authenticate, you can forward to 2083/2087/2096 internally.

Is it safe to allow port forwarding in SSH?

It can be, if you enforce SSH keys, limit users, and keep GatewayPorts no. Treat forwarding like a privileged feature, not a default convenience.

What’s the cleanest way for a team to share access?

Create named user accounts per person, require keys, and use an SSH jump host if you manage many servers. Avoid sharing one key across the team.

What if I get locked out while tightening firewall rules?

Keep one active SSH session open while changing rules, and test with a second session. If you do lose access, you’ll need console access from your hosting provider to revert rules.

SSH Port Forwarding Tutorial (2026): Securely Access cPanel, Webmail, and Database Ports on a VPS Without Exposing Them | HostMyCode