Server

FTP Active vs Passive, and Why SFTP Won

By Prabath Thalangama· October 24, 2026· 4 min read
#ftp#sftp#networking

Introduction

FTP is one of the oldest application protocols still in use, and its design — a control connection on port 21 plus a separate data connection for each transfer — is the source of nearly every problem people hit with it. Modern setups should use SFTP instead, but you still meet FTP on legacy gear, so it's worth understanding both.

Two connections

The control connection (client → server:21) carries commands (USER, PASS, LIST, RETR, STOR) and reply codes. The data connection carries the actual file bytes or directory listing, and is torn down after each transfer. How that data connection gets established is the active/passive distinction.

Active mode

  1. Client opens control to server:21.
  2. Client sends PORT 192,168,1,50,197,140 — "connect back to me at 192.168.1.50 port 50444" (197×256 + 140).
  3. Server opens the data connection from port 20 to the client's advertised address/port.

The problem: the server initiates an inbound connection to the client. If the client is behind NAT (almost always), the address in PORT is private and the server can't reach it. The client's firewall also sees an unsolicited inbound connection and drops it.

Passive mode

  1. Client opens control to server:21.
  2. Client sends PASV.
  3. Server replies 227 Entering Passive Mode (203,0,113,10,200,50) — "connect to me at 203.0.113.10 port 51250".
  4. Client opens the data connection to that server port.

Now the client initiates both connections, which works fine through client-side NAT. The burden moves to the server's firewall, which must allow inbound to a range of high ports.

Configuring passive on the server

# vsftpd.conf
pasv_enable=YES
pasv_min_port=40000
pasv_max_port=40100
pasv_address=203.0.113.10      # the PUBLIC IP, if the server itself is behind NAT

Then open 40000–40100/tcp inbound on the firewall, plus 21. With only ~100 ports you cap concurrent transfers — size the range for your load.

FTPS vs SFTP — not the same thing

  • FTPS = FTP + TLS. Still two connections, still PASV, still a port-range headache — now worse, because a NAT device can't rewrite the PASV reply (it's encrypted), so it can't help. You must set pasv_address and open the range manually.
  • SFTP = SSH File Transfer Protocol. A completely different protocol that runs as a subsystem inside a single SSH connection on port 22. One port, encrypted, authenticated by SSH (keys!), no data-channel gymnastics. Nothing to do with FTP beyond the name.

SFTP won because it's one TCP connection to one well-understood port, it piggybacks on SSH's authentication and encryption, and firewalls already handle SSH. There is essentially no reason to deploy new FTP or FTPS.

Hands-on

# SFTP with a key (recommended)
sftp -i ~/.ssh/deploy_key [email protected]
# non-interactive batch
sftp -b - [email protected] <<'EOF'
put build/app.tar.gz /var/www/releases/
chmod 644 /var/www/releases/app.tar.gz
EOF

# Restrict a user to SFTP-only, chrooted (sshd_config)
Match User sftponly
    ChrootDirectory /srv/sftp/%u
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no

The chroot directory must be owned by root and not group/world-writable, or sshd refuses the session.

# If you must debug real FTP
ftp -pv ftp.example.com          # -p forces passive, -v shows the dialogue
lftp -e "set ftp:passive-mode on; ls; bye" ftp.example.com

Verification and troubleshooting

  • Control connects, LIST or download hangs then times out — the classic data-connection failure. Client behind NAT + active mode → switch to passive. Passive + server firewall not opening the port range → open it and set pasv_address.
  • Passive works internally, fails from the internet — pasv_address not set to the public IP, so the server hands out its private IP in the 227 reply.
  • FTPS passive totally broken — NAT can't rewrite the encrypted PASV reply. Set pasv_address + open the range; there's no auto-help.
  • 425 Can't open data connection — firewall dropped the data connection, or an FTP ALG (application-layer gateway) on a router is "helpfully" mangling things. Disabling the router's FTP ALG often fixes more than it breaks.
  • SFTP Received message too long — the login shell is printing output (a banner, an echo in .bashrc). SFTP needs a clean channel; use ForceCommand internal-sftp or move interactive output behind a tty check.
  • SFTP chroot: "broken pipe" / immediate disconnect — ChrootDirectory ownership/permissions wrong (must be root-owned, not writable by others).
PrabathStuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.