I've been using SSH for almost my entire career, and tools like WinSCP and PuTTY have become essential to my daily workflow. Yet even after all these years, researching and writing this article taught me new techniques that I'm now incorporating into my own practices. If someone like me can still learn after decades of SSH use, I'm confident you'll find valuable insights here too.
The SSH Story: Born from a Security Crisis
In 1995, a password-sniffing attack struck the network at Helsinki University of Technology in Finland. For researcher Tatu Ylönen, this was the final straw. The existing remote access tools - telnet, rlogin, and FTP - transmitted everything in plaintext, including passwords. Anyone monitoring network traffic could steal credentials effortlessly.
Rather than accept this insecurity, Ylönen spent three months teaching himself cryptography and network programming. In July 1995, he released SSH 1.0 (Secure Shell) as free software. The timing was perfect - system administrators worldwide were desperate for a secure alternative. By the end of 1995, SSH had exploded to 20,000 users across 50 countries.
An interesting detail: Ylönen chose port 22 for SSH because it sits right between telnet (port 23) and FTP (port 21) - a symbolic replacement for both insecure protocols. In December 1995, he founded SSH Communications Security to commercialize the protocol, but the open-source implementation (OpenSSH) eventually became the dominant version used today.
The PuTTY Story: Born from Procrastination
Fast forward to 1998. British programmer Simon Tatham (then 21 years old) was supposed to be studying for exams but found himself distracted by a more interesting problem. He had created a simple Telnet client called STel ("Simon's Telnet") back in 1996, but Windows still lacked a good SSH client.
While procrastinating on his studies in summer 1998, Tatham "accidentally" implemented an SSH backend for his Telnet client. He converted STel into a multi-protocol tool, added SSH-1 support, and renamed it "PuTTY" - a playful British term that references "tty" (teletype terminals). In early 1999, he published PuTTY on a web page as free, open-source software.
What started as exam-avoidance became one of the most downloaded Windows applications of all time. Even today, decades later and despite Windows now including native SSH support, PuTTY remains the trusted SSH client for millions of Windows users worldwide.
Two Tools, One Legacy
Both SSH and PuTTY emerged from individual developers solving real problems they faced. Neither was created by a corporation or funded by venture capital - just talented programmers scratching their own itch. Their decision to release both tools as free software democratized secure remote access and fundamentally changed how we interact with servers and systems across the internet.
Want to master remote server access? SSH (Secure Shell) is the backbone of modern server administration, development workflows, and cloud infrastructure. Whether you're managing a VPS, deploying code, or tunneling through networks, SSH serves as your secure gateway to the digital world. This comprehensive guide will take you from basic connections to advanced tunneling, key authentication, and productivity-boosting configurations, transforming you from an SSH user into an SSH power user.
Every cloud server, Linux box, Raspberry Pi, and Docker container speaks SSH. It's the universal remote access protocol that replaced insecure telnet decades ago, encrypting everything: your commands, data, and passwords. Beyond simple server access, developers rely on SSH for Git operations, database tunneling, secure file transfers, and even as a lightweight VPN alternative.
A note for Windows users: While PuTTY has been the trusted SSH client for decades, Windows 10 and later versions now include OpenSSH natively. Throughout this guide, we'll cover both approaches so you can choose what works best for your workflow.
This article will guide you through a progressive journey from basic connections to advanced techniques. We'll explore key-based authentication that eliminates password typing, SSH config files that supercharge your productivity, port forwarding for secure tunneling, SOCKS proxies for routing traffic, jump hosts for accessing protected networks, efficient file transfer methods, and security hardening practices. By the end, you'll have the skills to handle any SSH scenario with confidence.
Let's start with the fundamental skill: connecting to a remote server. If you're using Linux, Mac, or modern Windows (10+), you already have OpenSSH installed and ready to use.
Basic connection syntax:
ssh username@hostname
ssh username@192.168.1.100
ssh user@example.com
Your first connection:
The first time you connect to a new server, SSH will ask you to verify the host's authenticity:
ssh root@example.com
# You'll see:
# The authenticity of host 'example.com (192.168.1.100)' can't be established.
# ECDSA key fingerprint is SHA256:xxx...
# Are you sure you want to continue connecting (yes/no)?
Type yes to proceed. This adds the server's fingerprint to your ~/.ssh/known_hosts file, preventing future warnings and protecting against man-in-the-middle attacks.
Connecting to non-standard ports:
Many servers run SSH on custom ports for added security. Simply specify the port with the -p flag:
ssh -p 2222 user@example.com
For PuTTY users on Windows:
If you're using the classic PuTTY client, here's how to connect:
example.com22 (or your custom port)While password authentication works, it's increasingly outdated. SSH keys offer superior security, greater convenience, and are required by most modern cloud providers. Once configured, you'll never type a password again, yet your connections will be more secure than ever.
Creating an SSH key pair is straightforward. The process generates two mathematically linked keys: a private key that stays on your computer, and a public key that you share with servers.
On Linux, Mac, or Windows with OpenSSH:
# Generate a new key pair using the modern ed25519 algorithm
ssh-keygen -t ed25519 -C "your_email@example.com"
# You'll be prompted with:
# Generating public/private ed25519 key pair.
# Enter file in which to save the key (/home/user/.ssh/id_ed25519):
# Enter passphrase (empty for no passphrase):
Best practices for key generation:
ed25519: This modern algorithm is more secure, faster, and uses smaller keys than RSAssh-keygen -t rsa -b 4096~/.ssh/ keeps things organizedUnderstanding what you've created:
~/.ssh/id_ed25519 - Your private key (guard this with your life!)~/.ssh/id_ed25519.pub - Your public key (safe to share with any server)Now that you have a key pair, you need to install your public key on any server you want to access. There are two methods, depending on your preference and system capabilities.
Method 1: The automatic way (recommended)
The ssh-copy-id utility handles everything for you:
ssh-copy-id user@example.com
# Automatically copies your public key to the server's ~/.ssh/authorized_keys file
Method 2: The manual approach
If ssh-copy-id isn't available, you can do it manually:
# First, display your public key
cat ~/.ssh/id_ed25519.pub
# SSH into your server using your password one last time
ssh user@example.com
# Once connected, run these commands:
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "ssh-ed25519 AAAA... user@host" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Testing your passwordless authentication:
Now disconnect and reconnect:
ssh user@example.com
# No password prompt - you're in instantly!
If you're using PuTTY, the process is slightly different but equally straightforward:
my_key.ppk~/.ssh/authorized_keys fileConnecting with your PuTTY key:
my_key.ppk fileIf you find yourself typing long SSH commands repeatedly, you're working too hard. The ~/.ssh/config file is SSH's secret weapon - it lets you create simple aliases for complex connections, set defaults, and save countless keystrokes every day.
Creating your SSH config file:
Open or create ~/.ssh/config in your text editor and add configurations like these:
# Personal VPS
Host personal
HostName example.com
User myuser
Port 22
IdentityFile ~/.ssh/id_ed25519
# Work server with custom port
Host work
HostName 192.168.1.100
User admin
Port 2222
IdentityFile ~/.ssh/work_key
# GitHub configuration
Host github.com
User git
IdentityFile ~/.ssh/github_key
# Apply settings to all AWS servers using wildcards
Host *.amazonaws.com
User ec2-user
IdentityFile ~/.ssh/aws_key
StrictHostKeyChecking no
The magic of simple aliases:
Instead of typing lengthy commands, you can now connect with simple aliases:
ssh personal # Replaces: ssh -i ~/.ssh/id_ed25519 myuser@example.com -p 22
ssh work # Replaces: ssh -i ~/.ssh/work_key admin@192.168.1.100 -p 2222
This alone will save you thousands of keystrokes over your career.
Power user options:
Once you're comfortable with basic configs, you can add advanced options to optimize your connections:
Host myserver
HostName example.com
User admin
Port 2222
IdentityFile ~/.ssh/my_key
ForwardAgent yes # Forward your SSH agent through the connection
ServerAliveInterval 60 # Send keepalive packets every 60 seconds
ServerAliveCountMax 3 # Disconnect after 3 failed keepalives
Compression yes # Compress data (helpful on slow connections)
ControlMaster auto # Enable connection multiplexing
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600 # Keep master connection open for 10 minutes
These advanced settings can dramatically improve performance, especially when opening multiple connections to the same host.
Here's where SSH transforms from a simple remote access tool into a powerful networking Swiss Army knife. SSH can securely tunnel any TCP connection, letting you access services that would otherwise be unreachable or insecure.
Imagine you have a database running on a remote server that's not exposed to the public internet. With local port forwarding, you can access it as if it were running on your own machine.
Basic example - accessing a remote database:
ssh -L 3306:localhost:3306 user@server.com
# Now you can connect to the remote MySQL database locally:
mysql -h localhost -P 3306
# Your connection travels through an encrypted SSH tunnel!
Understanding the syntax:
-L signals local port forwarding3306 (first number) is the local port on your machinelocalhost:3306 (after colon) is the destination from the server's perspectiveAccessing a web admin interface securely:
ssh -L 8080:localhost:80 user@server.com
# Open your browser to: http://localhost:8080
# You're now securely viewing the server's web interface
Accessing services on other machines through the SSH server:
ssh -L 5432:database-server:5432 user@jump-server.com
# This tunnels through jump-server to reach database-server
This technique is invaluable when you need to access services behind firewalls or on private networks.
Remote port forwarding works in the opposite direction. It allows you to expose a service running on your local machine to a remote server. This is particularly useful when you're behind a firewall or NAT.
Sharing your local development server:
ssh -R 8080:localhost:3000 user@public-server.com
# Anyone accessing public-server.com:8080
# will be tunneled to your localhost:3000
This is perfect for demos - you can show your local development site to a remote colleague without deploying anything to a staging server.
Dynamic port forwarding turns your SSH connection into a SOCKS proxy, essentially creating a lightweight VPN that routes all your traffic through the remote server.
Creating a SOCKS proxy:
ssh -D 8080 user@server.com
Configuring your browser:
Set your browser to use a SOCKS5 proxy with these settings:
localhost8080Now all your browser traffic routes securely through server.com. This is incredibly useful for:
Using SOCKS with command-line tools:
curl --socks5 localhost:8080 https://ifconfig.me
# The response shows the server's IP address, not yours!
You're not limited to one tunnel per connection. You can create multiple forwards simultaneously:
ssh -L 3306:localhost:3306 -L 5432:localhost:5432 -D 8080 user@server
# Database tunnel + PostgreSQL tunnel + SOCKS proxy - all in one SSH session!
In many corporate and production environments, sensitive servers aren't directly accessible from the internet. Instead, you must first connect to a "bastion host" or "jump server," then hop to your destination. SSH's ProxyJump feature makes this seamless.
The old, tedious approach:
ssh bastion
# Wait for connection...
# Then once you're in:
ssh production
# Finally connected, but what a hassle!
The modern ProxyJump method:
ssh -J bastion-user@bastion.com production-user@production-internal
# Connects directly through the bastion - transparent and automatic!
Chaining multiple jump hosts:
Some environments require multiple hops. No problem:
ssh -J jump1,jump2,jump3 final-destination
# SSH automatically traverses the entire chain
Making it permanent with SSH config:
Rather than typing complex ProxyJump commands, add this to your ~/.ssh/config:
Host production
HostName 10.0.0.50
User admin
ProxyJump bastion
Host bastion
HostName bastion.example.com
User bastion-user
IdentityFile ~/.ssh/bastion_key
Now connecting is simple:
ssh production
# SSH automatically routes through the bastion - you don't even notice!
This configuration makes accessing protected infrastructure as easy as connecting to a regular server.
SSH isn't just for remote command execution - it's also the foundation for secure file transfer. Let's explore the three primary methods, each suited to different use cases.
SCP (Secure Copy Protocol) is perfect for one-off file transfers when you need something quick and straightforward.
Uploading a file to a server:
scp file.txt user@server.com:/path/to/destination/
Downloading a file from a server:
scp user@server.com:/path/to/file.txt ./local/path/
Transferring entire directories:
Add the -r flag for recursive copying:
scp -r ./mydir user@server.com:/path/
Working through jump hosts:
Even file transfers can traverse jump hosts:
scp -o ProxyJump=bastion file.txt user@production:/path/
When you need to browse, navigate, and manage files interactively, SFTP (SSH File Transfer Protocol) provides a familiar FTP-like experience over a secure SSH connection.
sftp user@server.com
# Once connected, you can use these commands:
sftp> pwd # Show current remote directory
sftp> lpwd # Show current local directory
sftp> ls # List remote files
sftp> lls # List local files
sftp> get file.txt # Download a file
sftp> put file.txt # Upload a file
sftp> cd /path # Navigate remote directories
sftp> lcd /path # Navigate local directories
sftp> bye # Exit SFTP session
SFTP is ideal when you're exploring a server's file structure and transferring multiple files interactively.
For serious file synchronization, rsync is unbeatable. It only transfers the differences between source and destination, making it incredibly efficient for large directories or repeated syncs.
Basic directory synchronization:
rsync -avz -e ssh ./mydir/ user@server.com:/path/
Understanding the flags:
-a: Archive mode (preserves permissions, timestamps, and more)-v: Verbose output (shows what's being transferred)-z: Compress data during transfer-e ssh: Use SSH for the connectionCreating an exact mirror:
Add --delete to remove files on the destination that no longer exist in the source:
rsync -avz --delete -e ssh ./mydir/ user@server.com:/path/
This is perfect for maintaining backups, deploying websites, or keeping directories synchronized across systems.
If you've added passphrases to your SSH keys (which you should!), you might find it tedious to type them repeatedly. SSH Agent solves this by storing your decrypted keys in memory, requiring you to enter each passphrase just once per session.
Starting the SSH agent:
Most systems start the agent automatically, but if needed:
eval $(ssh-agent)
Adding your keys:
ssh-add ~/.ssh/id_ed25519
# Enter your passphrase once
# The agent remembers it for this session
Viewing loaded keys:
ssh-add -l
# Shows all keys currently held by the agent
Clearing all keys:
ssh-add -D
# Removes all keys from the agent
Automatic key loading:
To load your keys automatically when you log in, add this to your ~/.bashrc or ~/.zshrc:
if [ -z "$SSH_AUTH_SOCK" ]; then
eval $(ssh-agent -s)
ssh-add ~/.ssh/id_ed25519
fi
Now your keys are ready whenever you need them, without repeatedly typing passphrases.
Agent forwarding allows you to use your local SSH keys on remote servers without copying them. This is useful when you need to authenticate from a server to other systems.
Enabling agent forwarding:
ssh -A user@server.com
# Now from the remote server, you can:
git pull # Uses your local GitHub key!
ssh another-server # Uses your local SSH key!
Permanently enable in your config:
Host *
ForwardAgent yes
Important security caveat: Only use agent forwarding with servers you completely trust. While connected, anyone with root access on the remote server could potentially use your forwarded agent. For sensitive environments, consider alternative approaches like SSH certificates or bastion hosts.
Now that you understand SSH's capabilities, let's ensure you're using them securely. A misconfigured SSH server can be a major security vulnerability.
On your server, edit /etc/ssh/sshd_config to implement these security best practices:
# Disable root login
PermitRootLogin no
# Disable password authentication (keys only!)
PasswordAuthentication no
PubkeyAuthentication yes
# Change default port (security by obscurity, but helps)
Port 2222
# Only allow specific users
AllowUsers alice bob
# Limit authentication attempts
MaxAuthTries 3
# Disconnect idle sessions
ClientAliveInterval 300
ClientAliveCountMax 2
Restart SSH after changes:
sudo systemctl restart sshd
# Or on older systems:
sudo service ssh restart
Fail2Ban monitors SSH logs and automatically blocks IP addresses that show malicious behavior, like repeated failed login attempts.
Installing Fail2Ban:
# Install
sudo apt install fail2ban # Debian/Ubuntu
sudo yum install fail2ban # CentOS/RHEL
# Configure
sudo nano /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 22
maxretry = 3
bantime = 3600
# Start
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Google Authenticator for SSH:
# Install
sudo apt install libpam-google-authenticator
# Setup for user
google-authenticator
# Follow prompts, scan QR code
# Edit /etc/pam.d/sshd
auth required pam_google_authenticator.so
# Edit /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
# Restart
sudo systemctl restart sshd
Add to ~/.ssh/config:
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
Opening a new SSH connection involves a handshake that takes time. ControlMaster lets you reuse an existing connection, making subsequent connections nearly instant.
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# Don't forget to create the socket directory:
# mkdir -p ~/.ssh/sockets
The first connection establishes the master, and all subsequent connections reuse it. This is especially noticeable with tools like Git that make multiple SSH connections.
While dynamic port forwarding creates a SOCKS proxy, sshuttle goes further, transparently routing your network traffic through SSH without manual proxy configuration.
# Install sshuttle
pip install sshuttle
# Route all traffic through your SSH server
sshuttle -r user@server 0/0
# Or route only specific subnets
sshuttle -r user@server 10.0.0.0/8
This is incredibly useful for accessing internal corporate networks or routing around geo-restrictions.
You don't always need an interactive shell. SSH can execute commands and return the results directly:
Single command execution:
ssh user@server "df -h"
# Checks disk space and returns to your local terminal
Chaining multiple commands:
ssh user@server "cd /var/www && git pull && sudo systemctl restart nginx"
# Deploy and restart in one line
Complex scripts with heredocs:
ssh user@server << 'EOF'
cd /app
git pull
npm install
pm2 restart all
EOF
# Execute an entire deployment script remotely
One of the most frustrating experiences is losing work when your SSH connection drops. Terminal multiplexers like tmux and screen solve this by keeping your sessions alive on the server.
Quickly reattach to tmux:
ssh user@server -t tmux attach
# Instantly reconnects to your previous session
Automate it with SSH config:
Add this to ~/.ssh/config to automatically attach or create a tmux session:
Host dev
HostName dev.example.com
User devuser
RequestTTY yes
RemoteCommand tmux attach -t main || tmux new -s main
Now ssh dev automatically drops you into a persistent session. Disconnect and reconnect anytime without losing your work.
Sometimes you need to access a machine that's behind a firewall or NAT, with no way to connect inward. Reverse SSH solves this by having the remote machine establish an outbound connection to a public server.
On the firewalled machine:
ssh -R 2222:localhost:22 user@public-server.com
# Creates a tunnel back to this machine
# For a persistent connection, use autossh:
autossh -M 0 -R 2222:localhost:22 user@public-server.com
Connecting back from the public server:
ssh -p 2222 localhost
# You're now connected to the firewalled machine!
This technique is invaluable for accessing home labs, providing remote support, or managing machines behind restrictive firewalls.
.ppk fileLocal port forwarding:
3306localhost:3306Dynamic (SOCKS):
8080Modern options:
Even experienced users encounter SSH issues. Here's how to diagnose and fix the most common problems.
This error means the server rejected your authentication attempt. Here's how to debug it:
# First, verify your key is loaded in the agent
ssh-add -l
# Connect with verbose output to see what's happening
ssh -v user@server
# Look for lines saying "Offering public key" - this shows which keys SSH is trying
# Check file permissions on the server (incorrect permissions are a common culprit)
# ~/.ssh must be 700, authorized_keys must be 600
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
The server isn't accepting connections at all. Check these potential causes:
# Verify the SSH service is running
sudo systemctl status sshd
# Check if a firewall is blocking the connection
sudo ufw status
sudo firewall-cmd --list-all
# Confirm SSH is listening on the expected port
ss -tlnp | grep ssh
SSH limits the number of authentication attempts. If you have many keys loaded, you might hit this limit before the right key is tried.
# Specify exactly which key to use
ssh -i ~/.ssh/specific_key user@server
# Or tell SSH to only try keys specified in config/command line
ssh -o IdentitiesOnly=yes user@server
This warning appears when a server's fingerprint has changed, which could indicate a security issue (or just a reinstalled server).
# Remove the old host key from your known_hosts file
ssh-keygen -R hostname
# Or manually edit ~/.ssh/known_hosts and delete the relevant line
Congratulations! You've journeyed from basic logins to advanced tunneling, security hardening, and productivity optimization. What once seemed like a simple remote access tool has revealed itself as a powerful Swiss Army knife for secure networking.
Your SSH command reference:
Keep these commands handy - they cover 90% of real-world SSH use cases:
ssh user@host # Basic connection
ssh -i key user@host # Authenticate with specific key
ssh -p 2222 user@host # Connect to custom port
ssh -L 3306:localhost:3306 user@host # Local port forwarding (tunnel)
ssh -R 8080:localhost:3000 user@host # Remote port forwarding (expose local service)
ssh -D 8080 user@host # Dynamic forwarding (SOCKS proxy)
ssh -J jump user@host # Jump through bastion host
scp file user@host:/path # Quick file copy
rsync -avz -e ssh ./dir user@host:/path # Intelligent directory sync
Recommended next steps:
Create a comprehensive ~/.ssh/config - Add entries for all your frequently accessed servers. Future you will thank present you.
Eliminate passwords everywhere - Switch every remaining password-based connection to key authentication. It's both more secure and more convenient.
Master persistent sessions - Learn tmux or screen to never lose work to a dropped connection again.
Explore mosh - If you work over unreliable connections (mobile, coffee shop WiFi), check out mosh (mobile shell) for a better experience.
Consider SSH certificates - For organizations managing many servers, SSH certificates (via SSH CA) scale far better than distributing individual keys.
Automate with Ansible - Combine your SSH knowledge with Ansible to automate server configuration and deployment.
Pro tip: Your SSH config is just text - keep it in a private Git repository and sync it across all your machines for consistent access everywhere.
SSH isn't just a tool - it's your secure gateway to the entire internet's infrastructure. Every major cloud platform, every production server, every development environment speaks SSH. By mastering it, you've mastered one of the fundamental protocols that powers the modern internet.
The skills you've learned here will serve you throughout your career, whether you're a developer, system administrator, security engineer, or DevOps practitioner. SSH is timeless - the investment you've made in learning it will pay dividends for decades.
Happy tunneling!
Want to take your skills to the next level? For organizations with more than a handful of servers, SSH Certificate Authorities provide a elegant solution to key management. Instead of distributing public keys to every server, you sign short-lived certificates that grant time-limited access. It's how the pros handle SSH at scale.
Start exploring with: ssh-keygen -s for certificate signing, and research how to configure sshd to trust your CA. It's a game-changer for infrastructure security.
Historical information about SSH and PuTTY: