{"id":584,"date":"2026-09-09T16:56:57","date_gmt":"2026-09-09T16:56:57","guid":{"rendered":"https:\/\/www.vps.tc\/blog\/?p=584"},"modified":"2026-09-09T09:40:35","modified_gmt":"2026-09-09T09:40:35","slug":"what-is-ftp-file-transfer-protocol-explained","status":"publish","type":"post","link":"https:\/\/www.vps.tc\/blog\/en\/what-is-ftp-file-transfer-protocol-explained\/","title":{"rendered":"What Is FTP? File Transfer Protocol Explained"},"content":{"rendered":"<div class=\"aiw-toc\" style=\"border:1px solid #dbe3ea;border-radius:8px;padding:16px 20px;margin:0 0 28px\"><strong>Table of Contents<\/strong><\/p>\n<ol style=\"margin:10px 0 0;padding-left:22px\">\n<li><a href=\"#ftp-still-works-but-it-asks-more-from-your-firewall\">FTP still works, but it asks more from your firewall<\/a><\/li>\n<li><a href=\"#what-does-ftp-actually-provide\">What does FTP actually provide?<\/a><\/li>\n<li><a href=\"#how-an-ftp-connection-works\">How an FTP connection works<\/a><\/li>\n<li><a href=\"#active-and-passive-ftp-are-different-firewall-problems\">Active and passive FTP are different firewall problems<\/a><\/li>\n<li><a href=\"#ftp-ftps-and-sftp-are-not-interchangeable\">FTP, FTPS, and SFTP are not interchangeable<\/a><\/li>\n<li><a href=\"#downloading-and-uploading-files-from-the-command-line\">Downloading and uploading files from the command line<\/a><\/li>\n<li><a href=\"#security-checks-i-make-before-exposing-ftp\">Security checks I make before exposing FTP<\/a><\/li>\n<li><a href=\"#troubleshooting-the-errors-i-see-most-often\">Troubleshooting the errors I see most often<\/a><\/li>\n<li><a href=\"#when-ftp-still-makes-sense\">When FTP still makes sense<\/a><\/li>\n<li><a href=\"#frequently-asked-questions\">Frequently asked questions<\/a><\/li>\n<li><a href=\"#sources\">Sources<\/a><\/li>\n<\/ol>\n<\/div>\n<h2 id=\"ftp-still-works-but-it-asks-more-from-your-firewall\">FTP still works, but it asks more from your firewall<\/h2>\n<p>I usually meet FTP in the least convenient places: an old deployment workflow, a hosting panel, or a vendor integration that has been running unchanged for years. The login succeeds, then the directory listing hangs. That is when FTP&#8217;s two-connection design stops being an abstract protocol detail and becomes a real troubleshooting problem.<\/p>\n<p>FTP stands for <em>File Transfer Protocol<\/em>. It moves files between a client and a server, using one connection for commands and another for directory listings and file data. The standard control connection normally uses TCP port 21, while the data connection uses a separate port negotiated during the session.<\/p>\n<p>Standard FTP can authenticate users, but it does not encrypt usernames, passwords, commands, or transferred files. That distinction matters before you create an account or open a port on a VPS.<\/p>\n<h2 id=\"what-does-ftp-actually-provide\">What does FTP actually provide?<\/h2>\n<p>FTP is a protocol, not a storage service. The FTP daemon and the operating system decide where files live, which users can access them, and how those accounts are restricted. FTP defines the conversation between the client and server; it does not decide the underlying disk layout or permissions.<\/p>\n<p>That separation is easy to miss when a hosting panel presents everything as one form. You enter a hostname, username, password, and directory, but several independent pieces must work together: DNS, the control connection, the data connection, firewall rules, and filesystem permissions.<\/p>\n<p>Small detail. It matters.<\/p>\n<h2 id=\"how-an-ftp-connection-works\">How an FTP connection works<\/h2>\n<p>An FTP session may look like one connection in a graphical client, but the protocol normally uses two. The control connection carries commands. The data connection carries directory listings and file contents. This design is the reason FTP becomes awkward behind firewalls and NAT.<\/p>\n<h3>The control connection<\/h3>\n<p>The client connects to TCP port 21 on the server. Usernames, passwords, directory changes, listing requests, and commands that start or finish a transfer travel through this channel. RFC 959 defines the core FTP commands and response behavior.<\/p>\n<p>The server normally answers with three-digit response codes. For example, <code>220<\/code> means the service is ready, <code>230<\/code> indicates successful authentication, and <code>530<\/code> means the login failed. A graphical client may show only a vague error, but these codes become useful when I read the client or server log.<\/p>\n<h3>The data connection<\/h3>\n<p>File contents and directory listings travel through a separate data connection. The way that connection is established depends on whether the client uses active or passive mode.<\/p>\n<ul>\n<li><strong>Active FTP:<\/strong> The client tells the server which client-side address and port should receive the data connection. The server then opens the connection toward the client.<\/li>\n<li><strong>Passive FTP:<\/strong> The client asks the server for a data port and connects to that port itself. This is usually easier when the client is behind NAT or a firewall.<\/li>\n<\/ul>\n<p>In traditional active-mode usage, the server may use TCP port 20 as the source port. Treating that as an unchanging rule is not correct: the connection behavior is negotiated through commands such as <code>PORT<\/code> and <code>EPRT<\/code>, and details vary between implementations. In passive mode, the server selects an available port from its configured range.<\/p>\n<h2 id=\"active-and-passive-ftp-are-different-firewall-problems\">Active and passive FTP are different firewall problems<\/h2>\n<table>\n<thead>\n<tr>\n<th>Feature<\/th>\n<th>Active FTP<\/th>\n<th>Passive FTP<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Who starts the data connection?<\/td>\n<td>The server connects to the address provided by the client.<\/td>\n<td>The client connects to the port provided by the server.<\/td>\n<\/tr>\n<tr>\n<td>Client firewall<\/td>\n<td>Incoming connections can cause problems.<\/td>\n<td>Usually works more easily on client networks.<\/td>\n<\/tr>\n<tr>\n<td>Server firewall<\/td>\n<td>TCP 21 and the required data behavior must be allowed.<\/td>\n<td>TCP 21 and the passive port range must be allowed.<\/td>\n<\/tr>\n<tr>\n<td>Use behind NAT<\/td>\n<td>The client may advertise an unusable address.<\/td>\n<td>Usually more compatible, provided the server&#8217;s public address and port range are correct.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Passive mode does not repair every FTP problem. If you run an FTP server on a VPS, opening the control port while forgetting the passive port range is a common mistake. The client logs in, reaches the directory, and then reports something like <em>directory listing failed<\/em>.<\/p>\n<p>Changing the password will not help there. The data connection is blocked.<\/p>\n<h2 id=\"ftp-ftps-and-sftp-are-not-interchangeable\">FTP, FTPS, and SFTP are not interchangeable<\/h2>\n<p>The names are similar, but the protocols work differently. FTPS adds TLS to FTP. SFTP is a separate file-transfer protocol that runs through SSH.<\/p>\n<table>\n<thead>\n<tr>\n<th>Protocol<\/th>\n<th>Underlying design<\/th>\n<th>Common connection point<\/th>\n<th>Encryption<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>FTP<\/td>\n<td>FTP control and data connections<\/td>\n<td>TCP 21, plus a separate data connection<\/td>\n<td>None by default<\/td>\n<\/tr>\n<tr>\n<td>FTPS<\/td>\n<td>FTP with TLS<\/td>\n<td>TCP 21, or TCP 990 for implicit FTPS<\/td>\n<td>Provided by TLS<\/td>\n<\/tr>\n<tr>\n<td>SFTP<\/td>\n<td>SSH subsystem<\/td>\n<td>TCP 22<\/td>\n<td>Provided by SSH<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>FTPS can protect FTP commands and file data with TLS. RFC 4217 describes this arrangement. The connection type still matters: explicit FTPS starts as an ordinary FTP connection and upgrades to TLS, while implicit FTPS expects TLS from the beginning. They are different configuration choices.<\/p>\n<p>SFTP is not an encrypted version of FTP. It is a separate protocol inside an SSH session. If I already have SSH access to a Linux VPS, SFTP is often the simpler choice because it normally uses one TCP connection. Firewall rules are easier to reason about.<\/p>\n<p>When I first installed an FTP server on a test VPS, I opened TCP 21 and forgot to define the passive port range. Login worked, but every directory listing timed out. The client log showed the passive port, which made the problem obvious: the password was fine, but the second connection could not get through. Once I allowed the configured range, transfers worked.<\/p>\n<p>For the wider role of network connections across protocol layers, see <strong><a href=\"https:\/\/www.vps.tc\/blog\/en\/demystifying-internet-protocols-from-tcp-ip-to-http-3\/\">Demystifying Internet Protocols: From TCP\/IP to HTTP\/3<\/a><\/strong>.<\/p>\n<h2 id=\"downloading-and-uploading-files-from-the-command-line\">Downloading and uploading files from the command line<\/h2>\n<p>Graphical clients hide many details behind fields for the server address, username, password, and connection type. On the command line, I can see more clearly what the client is doing.<\/p>\n<p>On Debian and Ubuntu, install a basic FTP client with:<\/p>\n<pre><code>sudo apt update\nsudo apt install ftp<\/code><\/pre>\n<p>This is enough for simple tests. For production work, I usually reach for <code>lftp<\/code>, which has more capable transfer and scripting commands.<\/p>\n<pre><code>sudo apt install lftp\nlftp ftp.example.com\nuser file_user\nls\nget report.zip -o \/tmp\/report.zip\nbye<\/code><\/pre>\n<p>After <code>user<\/code>, the client asks for the password interactively. Do not put passwords directly in commands. <code>get<\/code> downloads a remote file, while <code>-o<\/code> selects the local destination.<\/p>\n<p>Use <code>put<\/code> to upload a file:<\/p>\n<pre><code>lftp ftp.example.com\nuser file_user\nput .\/build\/site.zip -o \/incoming\/site.zip\nbye<\/code><\/pre>\n<p>The local file <code>.\/build\/site.zip<\/code> is sent to <code>\/incoming\/site.zip<\/code> on the remote server. The transfer can still fail because of remote-directory permissions, quotas, or filename restrictions.<\/p>\n<p>For a complete directory, <code>mirror<\/code> is useful:<\/p>\n<pre><code>lftp ftp.example.com\nuser file_user\nmirror --verbose .\/site \/incoming\/site\nbye<\/code><\/pre>\n<p>This sends the local <code>.\/site<\/code> directory to <code>\/incoming\/site<\/code>. To download in the other direction, use <code>mirror --verbose \/remote\/site .\/site<\/code>. I test with a small directory first, especially before mirroring a large tree. One misunderstood option can overwrite more files than intended.<\/p>\n<h3>Set passive mode explicitly when needed<\/h3>\n<p>Most clients use passive mode by default now, but I still check rather than assume. If <code>lftp<\/code> is not doing what I expect, I set it inside the session:<\/p>\n<pre><code>lftp ftp.example.com\nset ftp:passive-mode true\nuser file_user\nls\nbye<\/code><\/pre>\n<p>The <code>true<\/code> value tells the client to open the data connection toward the server. If the listing still fails, check the server&#8217;s passive range, public NAT address, and firewall rules together.<\/p>\n<p>You can also use <code>curl<\/code> for a single download or upload:<\/p>\n<pre><code>curl --ftp-pasv -u file_user ftp:\/\/ftp.example.com\/report.zip -o \/tmp\/report.zip\ncurl --ftp-pasv -u file_user --upload-file .\/site.zip ftp:\/\/ftp.example.com\/incoming\/site.zip<\/code><\/pre>\n<p>If you provide only the username with <code>-u<\/code>, curl asks for the password interactively. In automated jobs, pay attention to credential storage: shell history, process listings, and CI\/CD logs can expose passwords unintentionally.<\/p>\n<h2 id=\"security-checks-i-make-before-exposing-ftp\">Security checks I make before exposing FTP<\/h2>\n<p>The main problem with FTP is not simply its age. Ordinary FTP does not encrypt usernames, passwords, commands, or file contents. Someone observing the same network can capture that traffic.<\/p>\n<ul>\n<li><strong>Do not use ordinary FTP for sensitive data.<\/strong> Backups, customer data, configuration files, and passwords should not cross an unencrypted connection.<\/li>\n<li><strong>Prefer SFTP or FTPS.<\/strong> The right choice depends on what the provider supports and what your existing client tooling can use.<\/li>\n<li><strong>Create named users with limited permissions.<\/strong> Giving a file-transfer account unnecessary root access is not an acceptable shortcut.<\/li>\n<li><strong>Restrict the root directory.<\/strong> Use chroot or the isolation features provided by the FTP server so the account cannot browse the whole system.<\/li>\n<li><strong>Keep the passive range narrow.<\/strong> Define a suitable range and allow only those ports instead of exposing a large random range to the internet.<\/li>\n<li><strong>Watch the logs.<\/strong> Investigate failed logins, transfers at unusual hours, and unusually large numbers of file requests.<\/li>\n<li><strong>Use unique credentials.<\/strong> Where possible, move to an SFTP workflow using SSH keys instead of passwords.<\/li>\n<\/ul>\n<p>Connecting an FTP account directly to a website&#8217;s document root also needs care. If the account can write an executable file into a directory served by the web server, the issue is no longer just file transfer. Check uploaded extensions, MIME types, ownership, and the web server&#8217;s execution rules together.<\/p>\n<p>Before exposing an FTP server to the internet, I write down the traffic flow:<\/p>\n<ol>\n<li>Which address and control port will the client use?<\/li>\n<li>Will the connection be active or passive?<\/li>\n<li>Which TCP range will be open for passive mode?<\/li>\n<li>If the server is behind NAT, is its public address configured in the FTP service?<\/li>\n<li>Which local directory can each user access?<\/li>\n<li>How long will transfer logs be retained?<\/li>\n<\/ol>\n<p>If you cannot answer these questions, opening a port is not a configuration plan. It only moves the next failure somewhere else. On test servers behind NAT, I check the client log first, then the server connection log, and finally the firewall counters.<\/p>\n<h2 id=\"troubleshooting-the-errors-i-see-most-often\">Troubleshooting the errors I see most often<\/h2>\n<p>Many FTP errors are not caused by a wrong password. The transfer mode, blocked passive ports, an incorrect public address, and directory permissions appear more often in my day-to-day work.<\/p>\n<h3>Login works, but the directory listing fails<\/h3>\n<p>This usually means the control connection works while the data connection cannot be established. Look for <code>Entering Passive Mode<\/code> or a <code>227<\/code> response in the client log. If the response contains a private address, the server may be behind NAT and advertising an address outside clients cannot reach.<\/p>\n<p>On a Linux server, I start with:<\/p>\n<pre><code>sudo ss -ltnp\nsudo nft list ruleset\nsudo journalctl -u vsftpd --since today<\/code><\/pre>\n<p><code>ss<\/code> shows listening TCP ports, <code>nft<\/code> displays firewall rules, and <code>journalctl<\/code> shows service logs. Seeing TCP 21 in the output proves only that the control service is listening. It says nothing about a passive transfer.<\/p>\n<h3><code>530 Login incorrect<\/code><\/h3>\n<p>This response can indicate a bad username or password, but account policies can produce the same result. Check the user&#8217;s shell, whether the account is locked, whether the home directory exists, and whether the FTP server permits that user. Read the server log before generating more failed attempts.<\/p>\n<h3>The transfer stops halfway<\/h3>\n<p>File size, connection timeouts, disk quotas, and idle timeouts on network devices can all contribute. Check disk space with <code>df -h<\/code> and inode usage with <code>df -i<\/code>. I once looked only at capacity on a VPS and missed that the inodes were exhausted; a directory full of tiny files was preventing new transfers.<\/p>\n<pre><code>df -h\ndf -i\nncdu \/var<\/code><\/pre>\n<p><code>ncdu<\/code> quickly shows which directories consume space. Before restarting a large transfer repeatedly, check both available capacity and filesystem inodes. Repeating the same failed command is not troubleshooting.<\/p>\n<h2 id=\"when-ftp-still-makes-sense\">When FTP still makes sense<\/h2>\n<p>There are situations where you cannot simply replace FTP. A legacy application may integrate only through FTP, a hosting panel may offer FTPS, or an external system may define FTP as its delivery protocol. If the protocol cannot change, isolate the connection, restrict access, and use TLS where possible.<\/p>\n<p>For a new Linux server, SFTP is my first choice. If SSH is already used for administration, I can transfer files without running another FTP daemon. For website deployment, Git-based deployment, CI\/CD, or a provider&#8217;s secure transfer option usually gives me a more controlled workflow.<\/p>\n<p>Do not confuse FTP uploads with the web server&#8217;s HTTP traffic. Uploading a file through FTP does not mean it will be served safely by a browser. HTTPS certificates and web-server configuration are separate concerns; <strong><a href=\"https:\/\/www.vps.tc\/blog\/en\/install-ssl-certificate-on-vps-lets-encrypt\/\">How to Install an SSL Certificate on a VPS with Let&#8217;s Encrypt<\/a><\/strong> covers that web-side setup.<\/p>\n<p>Bandwidth deserves a place in the plan too. Moving large backups during busy hours can affect response times for a web application on the same VPS. The measurement approach in <strong><a href=\"https:\/\/www.vps.tc\/blog\/en\/vps-bandwidth-explained-how-much-do-you-need\/\">VPS Bandwidth Explained: How Much Do You Need?<\/a><\/strong> is useful when scheduling this kind of work.<\/p>\n<h2 id=\"frequently-asked-questions\">Frequently asked questions<\/h2>\n<h3>Is FTP secure?<\/h3>\n<p>Ordinary FTP is not considered secure because it does not encrypt usernames, passwords, or file contents. For sensitive data, use SFTP or FTPS and restrict both permissions and logging.<\/p>\n<h3>Which port should be opened for FTP?<\/h3>\n<p>TCP 21 is commonly used for the control connection. With passive FTP, the server&#8217;s configured data-port range must also be allowed through the firewall; opening only TCP 21 is not enough for most passive transfers.<\/p>\n<h3>What is the main difference between FTP and SFTP?<\/h3>\n<p>FTP uses its own control and data connections. SFTP is a separate protocol that runs over SSH and usually uses one connection on TCP port 22.<\/p>\n<h3>When should passive mode be used in an FTP client?<\/h3>\n<p>Passive mode is usually easier when the client is behind NAT or a firewall because the client starts the data connection. It still depends on the server having the correct passive range and public address configured.<\/p>\n<p>My practical rule is simple: if you must keep FTP, test a real upload and download from outside the server&#8217;s network before calling the setup finished. A successful login is only the first connection.<\/p>\n<h2 id=\"sources\">Sources<\/h2>\n<ul class=\"aiw-sources\">\n<li><a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc959\" target=\"_blank\" rel=\"noopener nofollow\">RFC 959 &#8211; File Transfer Protocol<\/a> \u2014 rfc-editor.org<\/li>\n<li><a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc4217\" target=\"_blank\" rel=\"noopener nofollow\">RFC 4217 &#8211; Securing FTP with TLS<\/a> \u2014 rfc-editor.org<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>FTP moves files between clients and servers, but its separate control and data connections make firewalls, NAT, and security worth understanding.<\/p>\n","protected":false},"author":2,"featured_media":582,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[1863,1854,1857,43,1866,1860,29],"class_list":["post-584","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-what","tag-file-transfer","tag-ftp-en","tag-ftps-en","tag-linux","tag-networking","tag-sftp-en","tag-vps"],"lang":"en","translations":{"en":584,"tr":583},"pll_sync_post":[],"_links":{"self":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts\/584","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/comments?post=584"}],"version-history":[{"count":1,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts\/584\/revisions"}],"predecessor-version":[{"id":586,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts\/584\/revisions\/586"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/media\/582"}],"wp:attachment":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/media?parent=584"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/categories?post=584"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/tags?post=584"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}