SSH for Web Developers

SSH basics for Web Devs: Secure keys, config, and port forwarding explained

We may earn an affiliate commission through purchases made from our guides and tutorials.

Secure Shell (SSH) gives you an authenticated, encrypted way to run commands, copy files, forward ports, and use Git over untrusted networks. You will use it to deploy, debug, and automate. This guide gives you a practical baseline: install a client, create and protect keys, shape your ~/.ssh/config, forward ports, and harden the server—then tie it to your daily web workflow with Git and file transfer. By the end, you should be able to connect quickly and safely, and know where to tighten the bolts further.

Install a modern SSH client

Use OpenSSH everywhere. macOS and most Linux distros ship it. Windows 10/11 include an official OpenSSH client and server you can add via “Optional Features” or PowerShell; Microsoft documents installation and Windows-specific notes.

Verify the version. Aim for OpenSSH ≥ 8.2 to use FIDO2 (hardware) keys and newer defaults. Check with ssh -V; consult OpenSSH release notes for feature changes.

Once OpenSSH is available, your next step is identity—keys.

Generate a key you can trust

SSH uses asymmetric keys. You keep the private key; you place the public key on servers or developer services (e.g., GitHub/GitLab). On any platform with OpenSSH:

# Recommended software key
ssh-keygen -t ed25519 -C "you@example.com"

# Optional: hardware-backed key (FIDO2 security key required)
ssh-keygen -t ed25519-sk -C "you@example.com"

Ed25519 is compact and fast; the -sk variants produce keys tied to a FIDO2 authenticator and require a physical touch per use, which mitigates key theft on a compromised workstation. See ssh-keygen(1) and vendor guidance for FIDO2 details.

Protect the private key with a passphrase and load it into an agent:

# start the agent and add your key
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

Use agent forwarding sparingly; a compromised jump host can request signatures from your agent while your session is active. Favor per-host enabling, and disable by default.

With a key in hand, you need it on the remote side.

Install your public key on servers

The simplest path is ssh-copy-id:

ssh-copy-id user@host

It logs in once with your password and appends your public key to ~/.ssh/authorized_keys on the server. Man pages explain the exact behavior and flags.

Permissions matter. OpenSSH refuses to use keys if directories/files are too open:

# client side (your machine)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519

# server side (the account you log into)
chmod go-w ~/
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

On Windows, fix private-key ACLs via the file’s Security → Advanced dialog so only your user has access. Common “too open” errors and minimum modes are well-documented across platforms.

Next, you will want to stop typing long commands.

Tame connections with ~/.ssh/config

Create ~/.ssh/config (600) and describe hosts with clear aliases. The OpenSSH client reads options in order and applies the first match; the manual lists every directive.

Host web-prod
  HostName prod.example.com
  User deploy
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes
  ServerAliveInterval 30
  ServerAliveCountMax 3

# Jump through a bastion (ProxyJump)
Host api-* 
  User ubuntu
  ProxyJump bastion.example.com

# Reuse TCP connections (multiplexing) for speed
Host *
  ControlMaster auto
  ControlPath ~/.ssh/cm-%r@%h:%p
  ControlPersist 10m

ProxyJump simplifies traversing bastions; ControlMaster/ControlPersist multiplex multiple sessions over one TCP connection to reduce latency.

With these basics, connecting becomes a short alias: ssh web-prod. Now make the tunnel your web stack needs.

Forward ports for local development and admin

SSH can tunnel TCP safely. Three patterns matter:

  • Local forwarding exposes a remote service on your laptop:
    ssh -L 5432:127.0.0.1:5432 db@db.internal then connect to localhost:5432.
  • Remote forwarding exposes a local service to a remote host:
    ssh -R 9000:127.0.0.1:3000 user@host then access host:9000 remotely.
  • Dynamic forwarding (SOCKS) creates a local proxy for arbitrary connections:
    ssh -D 1080 user@jumphost, then point your app/browser at socks5://localhost:1080.

Authoritative guides explain each mode and when to use them; many teams use local forwarding for databases and dynamic forwarding to reach private subnets during incident response.

With connectivity solved, you need to move code and artifacts, and you likely use Git.

Use SSH with Git and CI

Register your public key with your forge, then set remotes to ssh:// or the short git@host:org/repo.git. GitHub and GitLab document setup, key testing, and commit signing over SSH. Hardware-backed keys also work once your OpenSSH and token support ed25519-sk/ecdsa-sk.

For CI/CD runners, prefer deploy keys or per-job ephemeral keys instead of reusing a personal key, and rotate regularly in line with NIST IR 7966’s key-lifecycle guidance.

Treating keys as credentials with a lifecycle reduces blast radius in compromised pipelines.

Copy files the right way

Use sftp or rsync over SSH for reliable transfers:

# one-off copy
scp ./build.tgz deploy@web-prod:/var/www/

# efficient sync with permissions and deletion
rsync -avz --delete ./public/ deploy@web-prod:/var/www/public/

Both ride the same SSH authentication you already configured; rsync minimizes bandwidth by sending deltas.

Next, harden the server side so your habits align with production controls.

Harden the SSH server baseline

On Linux or Windows Server with OpenSSH, make these defaults explicit in sshd_config:

  • Disable root logins: PermitRootLogin no (or prohibit-password where appropriate).
  • Disable password auth once keys are working for all admins: PasswordAuthentication no and, if used, ChallengeResponseAuthentication no.
  • Ensure PubkeyAuthentication yes.

These measures reduce credential-stuffing risk and enforce key-based access.

For Windows Server, Microsoft provides sshd configuration specifics; apply equivalent controls and test with a break-glass account before cutting over.

With policy tightened, consider operational hygiene and troubleshooting.

Operate safely day-to-day

Prefer least-privilege accounts plus sudo. Keep known_hosts current to detect host key changes. For agent use, forward only to trusted hosts and only when required; Teleport’s guidance and security community consensus highlight the risk of forwarding through untrusted intermediaries.

If your org manages many keys, adopt lifecycle controls—provision, inventory, rotate, and remove keys systematically—as urged by NIST IR 7966 and its bulletin. Treat unused keys as liabilities.

Next, here are the fastest fixes for common errors.

Troubleshoot quickly

  • “Permissions are too open” on private keys: set ~/.ssh to 700 and private keys to 600; on Windows, remove inherited ACLs and grant only your user.
  • “Permission denied (publickey)” on the server: tighten ~/ (go-w), ~/.ssh (700), and authorized_keys (600); confirm you copied the public key and are pointing the client at the private key. Use ssh -vvv to see which identities are tried.
  • Slow connects or repeated prompts: enable multiplexing (ControlMaster/ControlPersist) and IdentitiesOnly yes to avoid trying every key in your agent.

With reliability and security in hand, add two upgrades when your risk model demands them.

Two valuable upgrades

  • Hardware-backed keys (FIDO2). They require a physical touch, can enforce a PIN, and keep the private key in hardware—ideal for admins and high-value deployers. Generate with ssh-keygen -t ed25519-sk and enroll on GitHub/GitLab and servers that support it.
  • Jump hosts done right. Replace ad-hoc agent forwarding with ProxyJump and per-host policies in ~/.ssh/config. This keeps credentials local while still traversing private networks.

Quick Windows notes for web teams

Windows’ built-in OpenSSH behaves like the Unix client. Install via Features or PowerShell, then manage keys and ~/.ssh/config in your user profile (typically C:\Users\<you>\.ssh). Microsoft’s docs cover installation, key management, and server configuration details and gotchas.

A minimal starter ~/.ssh/config

# ~/.ssh/config (chmod 600)

Host bastion
  HostName bastion.example.com
  User ops
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes

Host web-*
  User deploy
  ProxyJump bastion
  IdentityFile ~/.ssh/id_ed25519
  ServerAliveInterval 30
  ServerAliveCountMax 3

Host *
  ControlMaster auto
  ControlPath ~/.ssh/cm-%r@%h:%p
  ControlPersist 10m

Usage: ssh web-01, rsync -avz ./public/ web-01:/var/www/public/, ssh -L 5432:127.0.0.1:5432 web-01.

Key takeaways

Start with OpenSSH and an Ed25519 key; prefer hardware-backed keys for privileged access. Store public keys on servers with ssh-copy-id, keep permissions strict, and codify hosts in ~/.ssh/config with ProxyJump and multiplexing. Use SSH for port forwarding, Git, and file transfer; then harden sshd to enforce keys and restrict root logins. Finally, manage key lifecycle as a first-class security control, not an afterthought. These practices reduce toil today and cut incident risk tomorrow.

Was this helpful?

Thanks for your feedback!
Alex is the resident editor and oversees all of the guides published. His past work and experience include Colorlib, Stack Diary, Hostvix, and working with a number of editorial publications. He has been wrangling code and publishing his findings about it since the early 2000s.

Leave a comment

Your email address will not be published. Required fields are marked *