FTP in Practice: Active vs Passive Mode, FTPS vs SFTP and Firewall Problems
Key takeaways
FTP control and data channels, Active vs Passive, FTPS and SFTP compared, firewall and NAT issues—beginner operations guide to file transfer.
Why FTP is still around in 2026
FTP (File Transfer Protocol) has been used since the 1970s for file exchange—still common in batch jobs, legacy systems, industrial gear, and large MFT workflows. It splits control and data channels and uses Active vs Passive modes, so firewall rules differ—often the source of “works locally, fails in prod.” By 2026, plaintext credentials make FTPS or SFTP the norm for anything security-sensitive. This article explains behavior, client/server configuration, and how to choose among FTP, FTPS, and SFTP.
Almost every FTP problem in practice comes from one design decision made before firewalls and NAT existed: FTP opens a separate TCP connection for every file transfer and directory listing, and it tells the other side where to connect by writing an IP address and port inside the control conversation. Firewalls and NAT devices only see ordinary connections and rewrite addresses in packet headers, not in the text of FTP commands. That mismatch explains why logins succeed while listings hang, why a server works on the LAN but not from outside, and why encrypting FTP (FTPS) can make firewall problems worse.
FTP from RFC 114 to RFC 959
RFC 114 through RFC 959
FTP dates to RFC 114 (1971); RFC 959 (1985) is the classic reference, later extended for IPv6, security (FTPS), and more. HTTP, S3, and cloud APIs displaced FTP for new greenfield systems, but mainframes, plant equipment, and legacy MFT still expose FTP.
TCP port 21, and why SFTP is not FTP
FTP is an application-layer protocol over TCP (control 21, data varies by mode). SFTP is not FTP—it runs over SSH.
Two channels and a stateful session
| Property | Description |
|---|---|
| Dual channel | Commands on control; file bytes on data. |
| Stateful session | Login, CWD, interactive dialogue. |
| Transfer modes | Stream, block, compressed (implementation-dependent). |
| Text commands | USER, PASS, RETR, STOR, … |
Control and data channels, active and passive
Control vs data
- Control (default 21/tcp): Auth, directory changes, transfer setup.
- Data: Actual LIST output and file payloads.
Active mode
- Client opens control to server 21.
- For transfer, client advertises a local data port (PORT).
- Server initiates data connection to the client (“active” server connect). Often fails behind client firewalls that block inbound data connections.
Passive mode
- Control connection same as above.
- Client sends PASV (or EPSV) and receives server IP:port for data.
- Client connects outbound to the server’s data port. Server must allow the passive port range in the firewall.
sequenceDiagram participant C as Client participant S as Server C->>S: Control connection C->>S: PASV S->>C: Data address and port C->>S: Data connection S->>C: File transfer
Seen on the wire, the PASV exchange explains the classic NAT failure. The server answers with 227 Entering Passive Mode (192,168,1,20,195,80): the first four numbers are the IP address and the last two encode the port as 195 × 256 + 80 = 50000. A server behind NAT that does not know its public address advertises its private address, 192.168.1.20, and a client on the internet then tries to connect there and times out, even though the control connection to the public address worked fine. Setting pasv_address (vsftpd) or its equivalent fixes it, as do clients that ignore the returned IP and reuse the control connection’s address (FileZilla and lftp both can).
EPSV (RFC 2428) avoids the problem at the protocol level: the reply 229 Entering Extended Passive Mode (|||50000|) carries only a port, and the client connects to the same host it already talks to. It also works with IPv6, where PASV cannot. Active mode fails for the mirror-image reason: the client’s PORT command advertises the client’s private address, and the server’s connection back to it is blocked by the client-side NAT or firewall.
Common commands (conceptual)
| Command | Meaning |
|---|---|
| USER / PASS | Login (plaintext—risky). |
| PWD / CWD | Path and cd. |
| LIST / NLST | Directory listing. |
| RETR | Download. |
| STOR / APPE | Upload / append. |
| TYPE | ASCII vs IMAGE (binary). |
| PASV / EPSV | Passive mode. |
lftp, vsftpd, and chroot jails
CLI client (lftp example)
Option names vary—check lftp docs and set -a.
lftp -u username,password ftp.example.com
curl -u user:pass 'ftp://ftp.example.com/pub/readme.txt' -o readme.txt
Both commands put the password on the command line, where it ends up in shell history and is visible to other users in the process list (ps aux). For scripts, store credentials in ~/.netrc (with chmod 600) and use curl --netrc or lftp’s bookmarks. With curl, add --ssl-reqd to require explicit FTPS; without it curl will happily fall back to plaintext if the server does not offer TLS. curl -v is also the quickest way to see the raw control dialogue (USER, PASS, EPSV, 229 ...), which is usually enough to tell which step fails.
vsftpd sketch (concept)
/etc/vsftpd.conf (paths vary by distro)
listen=YES
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
pasv_enable=YES
pasv_min_port=50000
pasv_max_port=50100
# important behind NAT: advertise the public address
# pasv_address=PUBLIC_IP
# FTPS (also needs rsa_cert_file / rsa_private_key_file)
ssl_enable=YES
Open 21/tcp and pasv_min–max in the firewall for stable Passive.
vsftpd’s parser is stricter than it looks: comments must be on their own lines, and there must be no spaces around =. A line like ssl_enable=YES # FTPS makes the whole value YES # FTPS, and the server refuses to start with 500 OOPS: bad bool value in config file for: ssl_enable. ssl_enable=YES alone is also not enough; vsftpd needs a certificate and key (rsa_cert_file, rsa_private_key_file), and force_local_logins_ssl=YES / force_local_data_ssl=YES to actually refuse plaintext logins. The passive range should be large enough for the number of simultaneous transfers you expect, since each open data connection holds one port.
Chroot and permissions
- Dedicated system accounts and chroot to limit writable roots.
- Minimum permissions on upload directories.
With chroot_local_user=YES, recent vsftpd versions refuse logins when the user’s home directory itself is writable, failing with 500 OOPS: vsftpd: refusing to run with writable root inside chroot(). The protection exists because a writable chroot root allows known escape tricks. The clean fix is to make the home directory owned by root and not writable, with a writable upload/ subdirectory inside it; allow_writeable_chroot=YES silences the check but gives up that protection.
Plain FTP vs FTPS vs SFTP
Plain FTP risks
Credentials and payloads can be sniffed on untrusted networks.
FTPS (FTP over TLS)
Explicit FTPS: AUTH TLS on the control channel, then encrypt. Avoid legacy implicit 990 when possible. Keep TLS versions and certificates current.
FTPS has a firewall side effect that surprises people who switch to it for security. Many firewalls and home routers contain an FTP “helper” (ALG) that reads the plaintext control channel, spots the PASV reply and temporarily opens the matching data port. Once the control channel is encrypted, the helper can no longer read it, so passive transfers that worked with plain FTP start timing out after the login. The fix is the same as for NAT: set an explicit passive port range on the server, open it in the firewall, and configure the public address. Another FTPS-specific failure is TLS session reuse: servers such as vsftpd (require_ssl_reuse=YES, the default) and FileZilla Server require the data connection to resume the control connection’s TLS session, and clients that do not support this fail the transfer with errors like 522 SSL connection failed: session reuse required.
SFTP (over SSH)
SFTP is not FTP. It runs inside SSH, with keys and encryption as first-class. Greenfield often picks SFTP.
| Aspect | FTPS | SFTP |
|---|---|---|
| Base | FTP + TLS | SSH |
| Ports | 21 + data range (complex) | 22 (simpler) |
| Legacy tools | Classic FTP clients | SSH clients |
For a new system where you control both ends, SFTP is usually the simpler choice, and the reason is operational rather than cryptographic: one port, one connection, key-based authentication, and nothing for NAT or firewalls to interpret. FTPS makes sense when the other side is a partner or device that only speaks FTP-family protocols, or when X.509 certificates are already the organization’s standard for identity. A common confusion is that the two are variants of one protocol; they are not. An FTP client cannot talk to an SFTP server, and “port 22 connection refused” in an FTP client usually means someone was given an SFTP address.
Where FTP still survives
| Scenario | Notes |
|---|---|
| Legacy integration | Banks, manufacturing batch exchanges, old MFT. |
| Large files | Clients with resume and parallel (lftp mirror). |
| Anonymous mirrors | Public FTP—needs abuse controls. |
| Device firmware | Some devices only expose FTP—isolate networks and prefer FTPS. |
Parallel transfers, resume, and binary mode
- Parallel transfers:
lftp mirror -P Nfor many files. - Resume: verify REST/RETR or client resume support.
- Binary mode: use TYPE I for images and zip to avoid corruption.
- Placement: reduce RTT with nearby relay servers.
Passive timeouts, failed transfers, and partial files
| Symptom | Check |
|---|---|
| Passive timeouts | Open PASV range on server firewall; set pasv_address behind NAT. |
| Active fails only | Client inbound data ports blocked—switch to Passive. |
| Listing works, transfer fails | Data channel firewall or FTPS data channel TLS settings mismatch. |
| Mojibake filenames | Client UTF-8, server OPTS UTF8 ON support. |
The server’s reply codes narrow the problem down faster than any table. 530 Login incorrect is an authentication problem (wrong password, a user in ftpusers deny lists, or a shell not listed in /etc/shells for PAM-based setups). 425 Can't open data connection or a hang right after 227/229 is almost always the data channel: firewall, NAT address or passive range. 550 means the path does not exist or permissions deny it, often because a chroot makes /home/user/files appear as /files. When I debug FTP, the first step is to run the same transfer with curl -v from the client’s network and read which reply code the dialogue stops at; the control log answers “login, listing or data?” in one attempt.
A quieter failure is partial files. FTP has no built-in integrity check, so an interrupted upload leaves a truncated file that downstream batch jobs may pick up as complete. Robust exchanges upload to a temporary name and rename it after the transfer (STOR data.tmp, then RNFR/RNTO), or ship a checksum or “done” marker file alongside the data.
Maintaining FTP while planning the move to FTPS or SFTP
FTP’s split channels and Active/Passive behavior interact tightly with firewalls and NAT, while plaintext is a legacy liability. FTPS hardens transport; SFTP or object APIs are the modern direction. Use this article for legacy FTP maintenance, Passive/NAT debugging, and roadmaps toward FTPS/SFTP.
References
- RFC 959, RFC 4217 (FTPS)
man lftp, OpenSSH SFTP documentation
Frequently Asked Questions (FAQ)
Q. Why are images and zip files corrupted after an FTP transfer while text files look fine?
A. The transfer ran in ASCII mode (TYPE A), where FTP is allowed to convert line endings between systems, which silently alters any byte sequences in binary files that look like newlines. Switch to binary/image mode (TYPE I, or binary in most command-line clients) before transferring non-text files. Many clients guess the mode from the file extension, so an explicit setting is safer in scripts.