FTP — the File Transfer Protocol — was standardised in 1985 as RFC 959 and is still in active use forty years later. Behind every FTP client you've ever used — FileZilla, WinSCP in FTP mode, the ftp command on macOS or Linux, the ftp.exe tool on Windows — is a small set of plain-text commands the client sends to the server, each followed by a numeric response code. This tutorial walks through all of them at a beginner level.
You don't need to know them by heart to use FTP — the GUI clients translate every drag-and-drop into the right sequence for you — but knowing what's actually being said is the difference between "the upload failed" and "the upload failed because the server returned 553 Could not create file, which means a permissions or quota issue on the destination directory."
How an FTP session is structured
An FTP session uses two network connections, which is the single most important thing to understand about the protocol:
- The control connection (port 21 by default) carries the commands and responses you type or your client sends on your behalf. It's open for the whole session.
- One or more data connections carry the actual file bytes and directory listings. Each transfer opens a fresh data connection and closes it when finished.
Every interaction follows a request/response pattern. You (or your client) send a command on the control connection; the server replies with a three-digit response code and a human-readable message. Codes that start with 1 mean "in progress", 2 means "success", 3 means "more input needed", 4 means "temporary failure", and 5 means "permanent failure".
Logging in
The first thing every FTP session does is authenticate. Two commands handle the whole exchange.
USER alice 331 Please specify the password. PASS hunter2 230 Login successful.
| Command | What it does |
|---|---|
| USER | Submits the account name. The server typically replies with 331 to ask for a password. |
| PASS | Submits the password. A successful login replies with 230; a bad password gets 530. |
| ACCT | Optional — supplies an account name for systems that distinguish user from billing account. Almost never used in modern deployments. |
| REIN | Re-initialise — logs the current user out without closing the connection. Useful for connection reuse, rare in practice. |
| QUIT | Closes the control connection politely. The server responds with 221. |
Moving around the server
| Command | What it does |
|---|---|
| PWD | Print working directory — the server replies with the current remote directory, e.g. 257 "/home/alice". |
| CWD | Change working directory. CWD /uploads moves to /uploads. |
| CDUP | Change to the parent directory — the equivalent of CWD .. |
Listing what's there
| Command | What it does |
|---|---|
| LIST | Long-form directory listing in a human-friendly format (similar to ls -l). The listing is sent on a freshly opened data connection. |
| NLST | Names only — one filename per line, no sizes or timestamps. Useful for scripts. |
| MLSD | Machine-parseable listing (RFC 3659). Each line is structured as fact=value;fact=value; filename. Scripts should prefer this over LIST when available. |
| MLST | Same machine-parseable format, but for a single file rather than a directory. |
| SIZE | Returns the size of one file in bytes. SIZE report.csv ⇒ 213 12345. |
| MDTM | Modification time of a file in UTC: MDTM report.csv ⇒ 213 20260616153000. |
Transferring files
This is the core of FTP. Each transfer uses a fresh data connection, which is set up immediately before the transfer (see "Active vs Passive" below).
| Command | What it does |
|---|---|
| RETR | Retrieve — downloads the named file from the server. RETR report.csv |
| STOR | Store — uploads to the named file on the server. STOR report.csv |
| APPE | Append — uploads, but adds to the file instead of overwriting it. |
| STOU | Store unique — uploads, but the server picks a unique filename and tells you what it chose. Avoids overwriting existing files. |
| REST | Restart — tells the server to begin the next transfer at byte offset N. Combined with RETR or STOR for resumable transfers. |
Managing files and directories
| Command | What it does |
|---|---|
| DELE | Delete a file. DELE old-report.csv |
| MKD | Make a directory. MKD archive |
| RMD | Remove a directory (must be empty on most servers). |
| RNFR + RNTO | Rename, in two steps. Send RNFR old-name, get 350, then send RNTO new-name. |
Transfer type and mode
FTP can interpret bytes as either text (and translate line endings between systems) or as a raw binary stream. Modern usage is almost always binary.
| Command | What it does |
|---|---|
| TYPE I | Image / binary mode — transfer bytes verbatim. The right choice for almost everything. |
| TYPE A | ASCII mode — the server translates line endings to match the destination platform. Useful only for plain-text files between heterogeneous systems. |
| STRU F | File structure (the default and only common choice today). The other STRU values are historical relics. |
| MODE S | Stream mode (the default). Other modes exist for block-oriented transfers but aren't supported in modern deployments. |
Active vs Passive: setting up the data connection
Before each transfer, the client and server need to agree on where the data connection will be. There are two patterns.
Active mode uses PORT: the client opens a listening socket on its own end and tells the server which IP and port to connect to. The server initiates the data connection. This pattern breaks behind most home and corporate firewalls because the server has to make an inbound TCP connection to the client.
Passive mode uses PASV: the server opens a listening socket and tells the client the IP and port. The client initiates both connections (control and data). This pattern works through most NATs and is the default in every modern client.
| Command | What it does |
|---|---|
| PORT | Active mode — tells the server the client's IP and port to connect to. |
| PASV | Passive mode — asks the server to open a port and report it. |
| EPRT / EPSV | IPv6-aware equivalents from RFC 2428. EPSV is what current FTP clients prefer. |
Server information and helpers
| Command | What it does |
|---|---|
| SYST | Asks the server what system it identifies as. Modern servers usually answer 215 UNIX Type: L8 regardless of the real OS. |
| STAT | Returns server status — running transfers, connection info, etc. |
| FEAT | Lists the optional extensions the server supports (MLST, SIZE, UTF8, etc.). See RFC 2389. |
| OPTS | Sets options for an extension. OPTS UTF8 ON is common. |
| HELP | Asks the server for a list of supported commands. |
| NOOP | No-operation — sends 200 OK back. Often used as a keep-alive ping. |
| SITE | Vendor-specific commands. SITE CHMOD 644 file is the most common. |
What to do next
If you want to feel the protocol directly, install a command-line FTP client like lftp (Linux), ncftp (Linux / macOS), or use the built-in ftp.exe on Windows. Most of them will let you turn on a verbose mode (set ftp:debug true in lftp, debug in ncftp) that prints every line of the underlying conversation as the client sends it. Watching one or two real sessions go by is the fastest way to make the table above stick.
When you're ready to move beyond FTP, read the SFTP tutorial next — SFTP runs over SSH and looks similar at the client level, but underneath it's a different protocol with very different security properties.