FTP Active vs Passive, and Why SFTP Won
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¶
- Client opens control to server:21.
- Client sends
PORT 192,168,1,50,197,140— "connect back to me at 192.168.1.50 port 50444" (197×256 + 140). - 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¶
- Client opens control to server:21.
- Client sends
PASV. - Server replies
227 Entering Passive Mode (203,0,113,10,200,50)— "connect to me at 203.0.113.10 port 51250". - 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 thePASVreply (it's encrypted), so it can't help. You must setpasv_addressand 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,
LISTor 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 setpasv_address. - Passive works internally, fails from the internet —
pasv_addressnot set to the public IP, so the server hands out its private IP in the227reply. - FTPS passive totally broken — NAT can't rewrite the encrypted
PASVreply. Setpasv_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, anechoin.bashrc). SFTP needs a clean channel; useForceCommand internal-sftpor move interactive output behind attycheck. - SFTP chroot: "broken pipe" / immediate disconnect —
ChrootDirectoryownership/permissions wrong (must be root-owned, not writable by others).
Related tools and reading¶
- On-site: Port Checker (21, 22, passive range), SSL Certificate Checker.
- Related posts: How NAT actually works, SMB versions and signing.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Network Protocols
DHCP DORA: How a Host Gets Its Address
The four DHCP packets, the renewal timers, why relays exist, and the tcpdump filter to watch it happen.
October 24, 2026SMB Versions and Signing, Demystified
SMB1 is a security liability, SMB3 encrypts and signs. Here's how dialect negotiation works and how to tell which one a connection actually used.
October 24, 2026DNS over TLS and DNS over HTTPS
Plain DNS is unencrypted and unauthenticated. DoT and DoH fix the transport — here's how each works, and the trade-off between privacy and network visibility.
October 24, 2026